Célula da tabela de acordeão – Como expandir / contratar dinamicamente o uitableviewcell?

Eu estou tentando criar um tipo de acordeão de uitableviewcell que, quando o usuário seleciona a célula, ele se expande para exibir uma visão de informações detalhada inline semelhante ao funcionamento do aplicativo digg. Inicialmente, tentei replace a tablecell atual por uma célula customizada em cellForRowAtIndex, mas a animação parece um pouco complicada, já que você pode ver a célula sendo substituída e, no geral, o efeito não funciona bem.

Se você olhar para o aplicativo digg e outros que fizeram isso, parece que eles não estão substituindo a célula atual, mas sim adicionando uma subvisão à célula? A célula original, no entanto, parece não se animar e apenas a nova visão se transforma na tabela.

Alguém tem alguma idéia de como conseguir um efeito semelhante?

Update: Eu fiz algum progresso usando o método neha abaixo e enquanto a célula está animando a maneira correta, está causando estragos com as outras células na tabela. O que eu fiz foi o UITableViewCell com uma class personalizada que contém uma instância de um UIView que realmente desenha a visão que eu então adiciono às células da tabela contentview.

- (void)setSelected:(BOOL)selected animated:(BOOL)animated { if (selected) { [self expandCell]; } } -(void)expandCell { self.contentView.frame = CGRectMake(0.0, 0.0, self.contentView.bounds.size.width, 110); } 

Aqui estão todos os methods de delegação de tabela que estou usando:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (isSearching && indexPath.row == selectedIndex) { static NSString *CellIdentifier = @"SearchCell"; CustomTableCell *cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } [cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]]; UILabel *theText = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 10.0, cell.contentView.bounds.size.width -20, 22.0)]; theText.text = @"Title Text"; [cell.contentView addSubview:theText]; UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0, 10 + 46.0, cell.contentView.bounds.size.width - 20, 40.0)]; textField.borderStyle = UITextBorderStyleLine; [cell.contentView addSubview:textField]; UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 88.0, cell.contentView.bounds.size.width - 20, 22.0)]; testLabel.text = [NSString stringWithFormat:@"Some text here"]; [cell.contentView addSubview:testLabel]; [theText release]; [textField release]; [testLabel release]; return cell; } else { static NSString *CellIdentifier = @"Cell"; CustomTableCell *cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } [cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:NO]; selectedIndex = indexPath.row; isSearching = YES; [tableView beginUpdates]; [tableView endUpdates]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (isSearching && indexPath.row == selectedIndex) { return 110; } return rowHeight; } 

Parece agora que a célula está se expandindo, mas não sendo realmente atualizada, de modo que os labels e o campo de texto não são mostrados. Eles, no entanto, aparecem quando eu rolar a célula e na canvas.

Alguma ideia?

A maneira da Apple de fazer é bem simples.

Primeiro, você precisará salvar a linha indexPath selecionada:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { self.selectedRowIndex = [indexPath retain]; [tableView beginUpdates]; [tableView endUpdates]; } 

Eu explicarei a parte atualizada e finalizada mais tarde.

Então, quando você tem o índice atualmente selecionado, você pode dizer ao tableView que ele deve dar àquela linha mais espaço.

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //check if the index actually exists if(selectedRowIndex && indexPath.row == selectedRowIndex.row) { return 100; } return 44; } 

Isso retornará a altura 100 para a célula selecionada.

Agora podemos voltar às atualizações de início / fim. Esse bloco aciona o recarregamento de toda a geometry tableView. Além disso, esse bloco é animado, o que acaba por dar a impressão de expansão da linha.

Espero que isso tenha sido útil, Pawel

O truque beginUpdates / endUpdates de Pawel é bom, e eu geralmente o uso. Mas, nesse caso, basta recarregar as linhas que estão mudando, garantindo que você as recarregue corretamente com o tipo de célula desejado e retorne a nova altura de célula correta.

Aqui está uma implementação completa do que eu acho que você está tentando realizar:

.h:

 #import  @interface ExpandingTableViewController : UITableViewController { } @property (retain) NSIndexPath* selectedIndexPath; @end 

.m:

 @implementation ExpandingTableViewController @synthesize selectedIndexPath; - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return 10; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier1 = @"Cell1"; static NSString *CellIdentifier2 = @"Cell2"; UITableViewCell *cell; NSIndexPath* indexPathSelected = self.selectedIndexPath; if ( nil == indexPathSelected || [indexPathSelected compare: indexPath] != NSOrderedSame ) { cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1] autorelease]; } cell.textLabel.text = [NSString stringWithFormat: @"cell %d", indexPath.row]; } else { cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier2] autorelease]; } cell.textLabel.text = [NSString stringWithFormat: @"cell %d", indexPath.row]; cell.detailTextLabel.text = [NSString stringWithFormat: @"(expanded!)", indexPath.row]; } return cell; } #pragma mark - #pragma mark Table view delegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if ( self.selectedIndexPath != nil && [self.selectedIndexPath compare: indexPath] == NSOrderedSame ) { return tableView.rowHeight * 2; } return tableView.rowHeight; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSArray* toReload = [NSArray arrayWithObjects: indexPath, self.selectedIndexPath, nil]; self.selectedIndexPath = indexPath; [tableView reloadRowsAtIndexPaths: toReload withRowAnimation: UITableViewRowAnimationMiddle]; } #pragma mark - #pragma mark Memory management - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)viewDidUnload { } - (void)dealloc { [super dealloc]; } @end 

Se você não quer recarregar a célula (você quer manter sua célula existente e apenas alterar o tamanho, e provavelmente adicionar / remover algumas subvisualizações), então simplesmente faça o truque beginUpdates / endUpdates em didSelectRowAtIndexPath :, e chame algum método em seu celular para incitar a mudança de layout. beginUpdates / endUpdates solicitará ao tableView para consultar novamente as alturas de cada célula – portanto, certifique-se de retornar o valor correto.

Crie uma class que subclass UITableviewcell em seu projeto. Crie a class ‘nib’ e defina seu pai para ser a class em seu projeto com tableview e substitua sua –

 (void)setSelected:(BOOL)selected animated:(BOOL)animated 

Escreva os methods contractCell () e expandCell () nessa class e forneça a altura das células desejadas no método expandCell. Chame esses methods adequadamente com base em alguns sinalizadores definidos para identificar quando a célula está no estado expandido ou no estado contratado. Use sua tableview

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 

método para manipular a seleção de células.

Substitua sua function cellForRowAtIndexPath por essa.

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (isSearching && indexPath.row == selectedIndex) { static NSString *CellIdentifier = @"SearchCell"; CustomTableCell *cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; [cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]]; UILabel *theText = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 10.0, cell.contentView.bounds.size.width -20, 22.0)]; theText.text = @"Title Text"; [cell.contentView addSubview:theText]; UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0, 10 + 46.0, cell.contentView.bounds.size.width - 20, 40.0)]; textField.borderStyle = UITextBorderStyleLine; [cell.contentView addSubview:textField]; UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 88.0, cell.contentView.bounds.size.width - 20, 22.0)]; testLabel.text = [NSString stringWithFormat:@"Some text here"]; [cell.contentView addSubview:testLabel]; [theText release]; [textField release]; [testLabel release]; return cell; } else { static NSString *CellIdentifier = @"Cell"; CustomTableCell *cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; [cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]]; return cell; } } 

crie o dictionary do wof da disposição que tem uma chave Select_sts que é 0 no começo quando estalar sua mudança 1 que acusa u tabela de mudança

 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 40.0)]; UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero]; headerLabel.backgroundColor = [UIColor clearColor]; headerLabel.opaque = NO; headerLabel.textColor = [UIColor blackColor]; headerLabel.highlightedTextColor = [UIColor whiteColor]; headerLabel.font = [UIFont boldSystemFontOfSize:16]; headerLabel.frame = CGRectMake(5.0, 10.0, 300.0, 20.0); headerLabel.text=[NSString stringWithFormat: @"PNR %@",[[record objectAtIndex:section] objectForKey:@"number"]]; customView.backgroundColor=[UIColor whiteColor]; btn_openClose.tag=section+10000; btn_openClose.backgroundColor=[UIColor clearColor]; // [btn_openClose setImage:[UIImage imageNamed:@"down_arrow.png"] forState:UIControlStateNormal]; [btn_openClose addTarget:self action:@selector(collapseExpandButtonTap:) forControlEvents:UIControlEventTouchUpInside]; [customView addSubview:btn_openClose]; } - (void) collapseExpandButtonTap:(id) sender{ int indexNo=[sender tag]-10000; // NSLog(@"total_record %@",[total_record objectAtIndex:indexNo]); NSMutableDictionary *mutDictionary = [[total_record objectAtIndex:indexNo] mutableCopy]; if([[mutDictionary objectForKey:@"Select_sts"] integerValue]==0) [mutDictionary setObject:[NSNumber numberWithInt:1] forKey:@"√"]; else [mutDictionary setObject:[NSNumber numberWithInt:0] forKey:@"Select_sts"]; [total_record replaceObjectAtIndex:indexNo withObject:mutDictionary]; // [table_view beginUpdates]; // [table_view reloadData]; // [table_view endUpdates]; NSMutableIndexSet *indetsetToUpdate = [[NSMutableIndexSet alloc]init]; [indetsetToUpdate addIndex:indexNo]; // [indetsetToUpdate addIndex:<#(NSUInteger)#>] // You can add multiple indexes(sections) here. [table_view reloadSections:indetsetToUpdate withRowAnimation:UITableViewRowAnimationFade]; }