Adicionando um número desconhecido de linhas ao UITableView ‘Células Estáticas’

Eu tenho uma tabela estática criada no Interface Builder com 6 seções, todas com diferentes quantidades de linhas. Eu agora quero adicionar uma 7ª seção com um número variável de linhas.

Em primeiro lugar, assim que descomente os methods delegates da tabela padrão que são inseridos pelo Xcode, recebo uma falha no self.tableView.tableHeaderView = containerView; onde eu adicionei um header à tabela.

Mais importante, eu estou recebendo uma falha com o seguinte 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]; } 

Como faço para deixar corretamente as seções existentes como elas são, mas adicionar uma extra com algumas células?

Para adicionar células dinâmicas a uma tabela de células estáticas, você deve replace todo método de delegação do UITableView que tenha um 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]; } } 

Apenas quando você tiver todo método substituído, sua mesa começará a funcionar. Para qualquer referenciação da seção estática, basta consultar [super].

A resposta de Darren me deu a ideia do que funcionou para mim, no entanto eu não tive que ir tão longe a ponto de implementar cada método delegado tableView. Você realmente só precisa replace numberOfRowsInSection e cellForRowAtIndexPath.

Primeiro eu defini uma tabela estática no Interface Builder com 4 seções, 2 a 4 células por seção. Eu queria que as seções 0, 2 e 3 fossem estáticas e parecessem exatamente como no IB, mas eu queria que a seção 1 tivesse um número personalizado de linhas com uma exibição personalizada em cada célula com base em uma matriz de valores que eu tinha.

No controlador de exibição da tabela estática, substitua o número de células retornadas para sua seção dinâmica, mas aceite os padrões para todas as outras seções (elas retornarão aos valores de IB). Faça o mesmo para cellForRowAtIndexPath e retorne a [super] implementação para todas as seções, exceto a seção 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 

E, claro, você precisa de uma célula 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 

Eu pensei em adicionar uma resposta atualizada baseada na excelente resposta do @ Darren. A maioria dos methods delegates não é necessária. Então, acabei de adicionar os necessários. Você pode facilmente adicionar uma célula personalizada, se desejar, mesmo usando um arquivo de nib. A imagem mostra uma tabela estática com 3 seções. A seção final é dinâmica de tempo de execução. Isso é extremamente útil. Isso está funcionando no ios7 BTW.

insira a descrição da imagem aqui

 #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"; } 

Vou postar resposta no Swift, mas também deve funcionar no Objective-C.

Na minha experiência, foi o suficiente para replace esses methods no 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 

Se você deseja ter uma célula de visualização de tabela personalizada na sua visualização de tabela, você precisa criar a subclass de UITableViewCell também com a ponta e registrá-la na visualização de tabela.

Meu controle inteiro é assim:

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

Eu acho que você vai ter que fazer o seu UITableView dynamic. Sendo que você tem um número “desconhecido” de linhas, você provavelmente irá definir o método delegado para algo como isto:

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

Eu descobri algo muito interessante e acho que vale mais a pena responder do que um “comentário”. Eu tinha este tableView estático com linhas dinâmicas funcionando e, em seguida, ele parou de funcionar. O motivo é simples. Eu já tive

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

e mais tarde decidi que queria / precisava de uma célula personalizada que eu projetasse no StoryBoard e só configurasse saídas para minha subclass UITableView. Então eu usei a outra técnica

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

O problema aqui parece ser que essa célula é reutilizada e, assim, você só verá uma das células por vez. Às vezes você nem verá nenhum, todos estarão vazios! Se você rolar, verá as outras células aparecendo logo depois, desaparecendo (mais como cintilação!).

Isso me deixou seriamente louca, até que percebi o que era (im) possível. Além disso, não tente fazer

[super.tableView dequeueReusableCellWithIdentifier:CellIdentifier]

porque, como mencionado por outras pessoas, isso sempre retorna nil em um tableView estático.

———

Então não tenho certeza do que fazer. Eu acho que vou usar a rota “prototipada estática”, que consiste em

  • Usando um Prototype Table View com Identificadores de Célula como “31” para a Seção 3, Linha 1. Eu posso fazer algo como
  NSString * identifier = [NSString stringWithFormat: @ "% d% d", indexPath.section, indexPath.row];
 cell = [tableVer dequeueReusableCellWithIdentifier: identificador]; 
  • Use Prototype Cells também para os headers. Eu uso "Cell1-Header" para o Cell Identifier do header da seção 1 e, em seguida, tenho algo como
  - (UIView *) tableView: (UITableView *) tableView seção viewForHeaderInSection: (NSInteger)
 {
     NSString * identifier = [NSString stringWithFormat: @ "Célula% d-Cabeçalho", seção];
     UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: identificador];
     return cell.contentView;
 } 

A coisa básica a ser tomada aqui é que você sempre pode começar com um tableView estático, mas no momento em que você perceber que precisará de algo dynamic, troque-o por Prototype (ele manterá suas linhas embora eu não me lembre do que ele faz com as seções!) e use esta técnica do KISS.

Acho que encontrei uma solução melhor e mais fácil, com seções ou linhas “fantom” no IB.

Caso você saiba o número máximo de células que você usaria na seção 7 (digamos 10), você deve definir o número de linhas para 10, quando você configura a seção 7 no IB.

Você não é forçado a usar todas as 10 linhas na seção, isso pode ser definido por

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

Por exemplo, se você retornar 5 quando a seção == 6 (na verdade, a seção 7), apenas 5 linhas serão exibidas.

Admito que não é dynamic no sentido absoluto da palavra, mas talvez resolva a maioria dos casos.