Celda de tabla de acordeón – ¿Cómo expandir / contraer dinámicamente uitableviewcell?

Estoy intentando crear un tipo de acordeón de uitableviewcell que, cuando el usuario selecciona la celda, se expande para mostrar una vista de información detallada en línea similar a cómo funciona la aplicación digg. Inicialmente traté de reemplazar la tabla actual con una celda personalizada en cellForRowAtIndex; sin embargo, la animación se ve un poco agitada ya que se puede ver que la celda está siendo reemplazada y, en general, el efecto no funciona bien.

Si nos fijamos en la aplicación digg y en otros que han hecho esto, parece que no reemplazan la celda actual, sino que tal vez agregan una subvista a la celda. Sin embargo, la celda original no parece animarse y solo los nuevos acordeones de vista en la tabla.

¿Alguien tiene alguna idea de cómo lograr un efecto similar?

Actualización: He avanzado un poco usando el método de Neha a continuación y mientras la celda está animando de la manera correcta está causando esgulps con las otras celdas en la tabla. Lo que he hecho es submuestrear UITableViewCell con una clase personalizada que contiene una instancia de una UIView que realmente dibuja la vista que luego agrego a la vista de contenido de las celdas de la tabla.

- (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); } 

Aquí están todos los métodos de delegado de mesa que estoy 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; } 

Ahora parece que la celda se está expandiendo, pero no se está actualizando, por lo que las tags y el campo de texto no se muestran. Sin embargo, aparecen cuando desplazo la celda y en la pantalla.

¿Algunas ideas?

La manera de hacer de Apple es bastante simple.

Primero, deberá guardar la fila indexPath seleccionada:

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

Explicaré la parte actualizada de inicio / final más tarde.

Luego, cuando tenga el índice seleccionado en ese momento, puede decirle a tableView que le debe dar a esa fila más espacio.

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

Esto devolverá la altura 100 para la celda seleccionada.

Ahora podemos volver a las actualizaciones de inicio / finalización. Ese bloque desencadena la recarga de toda la geometría tableView. Además, ese bloque está animado, lo que finalmente da la impresión de que las filas se están expandiendo.

Espero que esto haya sido útil, Pawel

El truco beginUpdates / endUpdates de Pawel es bueno, y lo uso a menudo. Pero en este caso, solo necesita volver a cargar las filas que están cambiando de estado, asegurándose de que las vuelva a cargar correctamente con el tipo de celda deseado y de que devuelva la altura de celda nueva correcta.

Aquí hay una implementación completa de trabajo de lo que creo que estás tratando de lograr:

.h:

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

.metro:

 @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 

Si no desea volver a cargar la celda (desea mantener su celda existente y simplemente cambiar el tamaño, y probablemente agregar / eliminar algunas subvistas), simplemente haga el truco beginUpdates / endUpdates en didSelectRowAtIndexPath :, y llame a algún método en su celular para incitar el cambio de diseño. beginUpdates / endUpdates solicitará a tableView que vuelva a consultar las alturas para cada celda, así que asegúrese de devolver el valor correcto.

Cree una clase que subclasifique UITableviewcell en su proyecto. Cree la punta de esta clase y configure su padre para que sea la clase en su proyecto con tabla vista y anule su –

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

Escriba los métodos contractCell () y expandCell () en esta clase y proporcione la altura de las celdas que desee en el método expandCell. Llame a este método de forma adecuada en función de algunos indicadores establecidos para identificar si la celda está en estado expandido o estado contraído. Use su tabla vista

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

método para manejar la selección de celdas.

Reemplace su función cellForRowAtIndexPath con esta.

  - (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; } } 

crear una matriz con diccionario que tenga una clave Select_sts que sea 0 en el inicio cuando haga clic en su cambio 1 accourding u change table

 - (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]; }