Agregar número desconocido de filas a UITableView de “células estáticas”

Tengo una tabla estática creada en Interface Builder con 6 secciones, todas con diferentes cantidades de filas. Ahora quiero agregar una séptima sección con un número variable de filas.

En primer lugar, tan pronto como elimine el comentario de los métodos de delegado de tabla estándar que Xcode inserta, obtengo un locking en self.tableView.tableHeaderView = containerView; donde he agregado un encabezado a la tabla.

Lo que es más importante, tengo un locking con el siguiente código

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 7; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section==6) { return 4; } else { return [super tableView:tableView numberOfRowsInSection:section]; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {/* if (indexPath.section == 6) { static NSString *CellIdentifier = @"cellWireless"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell... return cell; }*/ return [super tableView:tableView cellForRowAtIndexPath:indexPath]; } 

¿Cómo dejo correctamente las secciones existentes tal como están, pero agrego una más con algunas celdas?

Para agregar celdas dinámicas a una tabla de celdas estáticas, debe anular todos los métodos de delegación de UITableView que tengan indexPath.

 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath -(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 

.

 -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return NO; } -(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { return NO; } -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewCellEditingStyleNone; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { int section = indexPath.section; // if dynamic section make all rows the same height as row 0 if (section == self.dynamicSection) { return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; } else { return [super tableView:tableView heightForRowAtIndexPath:indexPath]; } } - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath { int section = indexPath.section; // if dynamic section make all rows the same indentation level as row 0 if (section == self.dynamicSection) { return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; } else { return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath]; } } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == self.dynamicSection ) { return [self.dataListArray count]; } else { return [super tableView:tableView numberOfRowsInSection:section]; } } -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { int section = indexPath.section; int row = indexPath.row; if (section == self.dynamicSection) { // make dynamic row's cell static NSString *CellIdentifier = @"Dynamic Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } cell.textLabel.text = [self.dataListArray objectAtIndex:row]; return cell; } else { return [super tableView:tableView cellForRowAtIndexPath:indexPath]; } } 

Solo cuando hayas anulado todos los métodos, tu mesa comenzará a funcionar. Para cualquier referencia a la sección estática, simplemente consulte [super].

La respuesta de Darren me dio la idea de lo que funcionó para mí, sin embargo, no tuve que ir tan lejos como para implementar cada método delegado tableView. Realmente solo necesita reemplazar numberOfRowsInSection y cellForRowAtIndexPath.

Primero definí una tabla estática en Interface Builder con 4 secciones, de 2 a 4 celdas por sección. Quería que las secciones 0, 2 y 3 fueran estáticas y se vean exactamente como lo hacían en IB, pero quería que la sección 1 tuviera un número personalizado de filas con una pantalla personalizada en cada celda en función de una matriz de valores que tenía.

En el controlador de vista para la tabla estática, anule la cantidad de celdas devueltas para su sección dinámica, pero acepte las predeterminadas para todas las demás secciones (volverán a los valores de IB). Haga lo mismo para cellForRowAtIndexPath y devuelva la implementación [super] para todas las secciones, excepto la sección 1.

 @implementation myMostlyStaticTableViewController @synthesize myFancyArray; - (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger) section { if (section == 1) return [myFancyArray count]; // the number of rows in section 1 else return [super tableView:tableView numberOfRowsInSection:section]; } - (UITableViewCell *) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath { // for cells not in section 1, rely on the IB definition of the cell if (indexPath.section != 1) return [super tableView:tableView cellForRowAtIndexPath:indexPath]; // configure a task status cell for section 1 MyCustomTableViewCell *cell; cell = [tableView dequeueReusableCellWithIdentifier:@"myCustomCell"]; if (!cell) { // create a cell cell = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"myCustomCell"]; } cell.myCustomLabel.text = [myFancyArray objectAtIndex:indexPath.row]; return cell; } @end 

Y, por supuesto, necesitas una celda personalizada:

 @implementation MyCustomTableViewCell - (UITableViewCell *) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { // initialize cell and add observers self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (!self) return self; self.clipsToBounds = YES; self.selectionStyle = UITableViewCellSelectionStyleNone; // configure up some interesting display properties inside the cell _label = [[UILabel alloc] initWithFrame:CGRectMake(20, 9, 147, 26)]; _label.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:17]; _label.textColor = [UIColor colorWithWhite:0.2 alpha:1]; [self.contentView addSubview:_label]; return self; } @end 

Pensé que agregaría una respuesta actualizada basada en la excelente respuesta de @Darren. La mayoría de los métodos de delegado no son necesarios. Entonces, acabo de agregar los obligatorios. Puede agregar fácilmente una celda personalizada si lo desea, incluso utilizando un archivo de punta. La imagen muestra una tabla estática con 3 secciones. La sección final es dinámica de tiempo de ejecución. Esto es extremadamente útil. Esto está funcionando en ios7 BTW.

enter image description here

 #define DYNAMIC_SECTION 2 #import "MyTableViewController.h" @interface MyTableViewController () @property (strong, nonatomic)NSArray *myArray; @end @implementation MyTableViewController - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { _myArray = @[@"ONE", @"TWO", @"THREE", @"FOUR"]; } return self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [super numberOfSectionsInTableView:tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section != DYNAMIC_SECTION) { return [super tableView:tableView numberOfRowsInSection:section]; } return [self.myArray count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section != DYNAMIC_SECTION) { return [super tableView:tableView cellForRowAtIndexPath:indexPath]; } static NSString *id = @"MyCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:id]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id]; } cell.textLabel.text = self.myArray[indexPath.row]; return cell; } // required -(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath { int section = indexPath.section; if (section == DYNAMIC_SECTION) { return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; } else { return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath]; } } // Not required - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (section != DYNAMIC_SECTION) { return [super tableView:tableView titleForHeaderInSection:section]; } return @"some title"; } 

Voy a publicar respuestas en Swift, pero también debería funcionar en Objective-C.

En mi experiencia, fue suficiente para anular estos métodos en UITableViewController :

 tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int 

Si desea tener una celda de vista de tabla personalizada en su vista de tabla, debe UITableViewCell subclase de UITableViewCell también con plumín y registrarla en su vista de tabla.

Mi controlador completo se ve así:

 var data = ["Ahoj", "Hola", "Hello"] override func viewDidLoad() { super.viewDidLoad() tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "reuseIdentifier") } // MARK: - Table view data source override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 1 { return data.count } return super.tableView(tableView, numberOfRowsInSection: section) } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { if indexPath.section == 1 { let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! CustomCell cell.titleLabel.text = data[indexPath.row] return cell } return super.tableView(tableView, cellForRowAtIndexPath: indexPath) } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 44 } override func tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int { return 0 } override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) if indexPath.section == 1 { print(data[indexPath.row]) } } @IBAction func addItem() { data.append("Item \(data.count)") tableView.beginUpdates() tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: data.count - 1, inSection: 1)], withRowAnimation: .Left) tableView.endUpdates() } @IBAction func removeItem() { if data.count > 0 { data.removeLast() tableView.beginUpdates() tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: data.count, inSection: 1)], withRowAnimation: .Left) tableView.endUpdates() } } 

Creo que vas a tener que hacer que tu UITableView sea dinámica. Siendo que tiene un número “desconocido” de filas, lo más probable es que establezca el método de delegado en algo como esto:

  - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [someArray count]; } 

Descubrí algo bastante interesante, creo, y vale más la pena que un “comentario”. Tenía este TableView estático con filas dinámicas funcionando, y luego dejó de funcionar. El motivo es simple Anteriormente tuve

[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]

y luego decidí que quería / necesitaba una Célula personalizada que diseñaba en mi StoryBoard y solo establecía salidas a mi Subescala UITableView. Entonces utilicé la otra técnica

[super tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.section]];

El problema aquí parece ser que esta celda se vuelve a utilizar y, por lo tanto, solo verá una de las celdas a la vez. En algún momento, incluso no verás ninguno, ¡todos estarán vacíos! Si te desplazas, verás que las otras celdas aparecen brevemente y luego desaparecen (¡más como parpadeo!).

Esto me volvía loco de verdad, hasta que me di cuenta de lo que era (im) posible. Además, no intentes hacer

[super.tableView dequeueReusableCellWithIdentifier:CellIdentifier]

porque como lo mencionan otras personas, esto siempre devuelve nil en un tableView estático.

Entonces no estoy seguro de qué hacer. Supongo que usaré la ruta “prototipo estático”, que consiste en

  • Usar una vista de tabla prototipo con identificadores de celda como “31” para la fila 3 de la sección 3. Entonces puedo hacer algo como
  NSString * identifier = [NSString stringWithFormat: @ "% d% d", indexPath.section, indexPath.row];
 cell = [tableView dequeueReusableCellWithIdentifier: identifier]; 
  • Usa células prototipo también para los encabezados. Yo uso "Cell1-Header" para el identificador de celda del encabezado de la sección 1 y luego tengo algo así como
  - (UIView *) tableView: (UITableView *) tableView viewForHeaderInSection: sección (NSInteger)
 {
     NSString * identifier = [NSString stringWithFormat: @ "Cell% d-Header", sección];
     UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: identifier];
     return cell.contentView;
 } 

Lo más básico es que siempre puedes comenzar con un TableView estático, pero en el momento en que te das cuenta de que vas a necesitar algo dynamic, cambiarlo a Prototype (mantendrá tus filas aunque no recuerdo lo que hace con las secciones!) y usa esta técnica KISS.

Creo que encontré una solución mejor y más fácil, con secciones o filas “fantom” en IB.

En caso de que conozca la cantidad máxima de celdas que usaría en la sección 7 (digamos 10), debe establecer el número de filas en 10, cuando configure la sección 7 en IB.

No está obligado a usar las 10 filas en la sección, esto se puede configurar por

  -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section. 

Por ejemplo, si devuelve 5 cuando la sección == 6 (en realidad, la 7ma sección), entonces solo se mostrarán 5 filas.

Reconozco que no es dynamic en el sentido absoluto de la palabra, pero quizás resuelve la mayoría de los casos.