objective-c - uitableviewcell - uitableview examples




Em um storyboard, como faço uma célula personalizada para uso com vários controladores? (4)

Apesar da grande resposta de BJ Homer, sinto que tenho uma solução. No que diz respeito ao meu teste, funciona.

Conceito: Crie uma classe personalizada para a célula xib. Lá você pode esperar por um evento de toque e executar a segue de forma programática. Agora tudo o que precisamos é de uma referência ao controlador que está executando o Segue. Minha solução é configurá-lo em tableView:cellForRowAtIndexPath:

Exemplo

Eu tenho um DetailedTaskCell.xib contendo uma célula da tabela que gostaria de usar em várias visualizações de tabela:

Existe uma classe personalizada TaskGuessTableCell para essa célula:

É aqui que a mágica acontece.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

Eu tenho vários Segues, mas todos eles têm o mesmo nome: "FinishedTask" . Se você precisa ser flexível aqui, sugiro adicionar outra propriedade.

O ViewController é assim:

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

Pode haver maneiras mais elegantes de conseguir o mesmo, mas funciona! :)

Estou tentando usar storyboards em um aplicativo em que estou trabalhando. No aplicativo há listas e usuários e cada um contém uma coleção do outro (membros de uma lista, listas de propriedade de um usuário). Portanto, eu tenho as classes ListCell e UserCell . O objetivo é que eles sejam reutilizáveis ​​em todo o aplicativo (ou seja, em qualquer um dos meus controladores de visualização de tabela).

É aí que estou correndo em um problema.

Como faço para criar uma célula tableview personalizada no storyboard que pode ser reutilizada em qualquer controlador de exibição?

Aqui estão as coisas específicas que eu tentei até agora.

  • No Controlador # 1, adicionamos uma célula protótipo, definimos a classe para minha subclasse UITableViewCell , definimos o ID de reutilização, adicionamos os rótulos e os conectamos às saídas da classe. No Controlador # 2, incluiu uma célula protótipo vazia, configurou-a para a mesma classe e reutilizou o id como antes. Quando é executado, os rótulos nunca aparecem quando as células são mostradas no Controlador # 2. Funciona bem no controlador # 1.

  • Projetou cada tipo de célula em um NIB diferente e conectou-se à classe de célula apropriada. No storyboard, adicionamos uma célula protótipo vazia e definimos sua classe e reutilizamos id para nos referirmos à minha classe de célula. Nos métodos viewDidLoad dos controladores, registrou esses arquivos NIB para o ID de reutilização. Quando mostrado, as células em ambos os controladores estavam vazias como o protótipo.

  • Mantive protótipos em ambos os controladores vazios e defini classes e reutilizei id para minha classe de célula. Construiu a interface do usuário das células inteiramente em código. As células funcionam perfeitamente em todos os controladores.

No segundo caso, suspeito que o protótipo esteja sempre substituindo o NIB e, se eu matei as células protótipo, o registro do meu NIB para o ID de reutilização funcionaria. Mas então eu não seria capaz de configurar seguidos das células para outros quadros, o que é realmente o objetivo de usar storyboards.

No final do dia, quero duas coisas: vincular os fluxos baseados em visualização de tabela no storyboard e definir os layouts de célula visualmente, e não no código. Eu não consigo ver como chegar a ambos até agora.


BJ Homer deu uma excelente explicação do que está acontecendo.

Do ponto de vista prático, eu acrescentaria que, dado que você não pode ter células como xibs E conectar segues, o melhor para escolher é ter a célula como um xib - as transições são muito mais fáceis de manter do que layouts de célula e propriedades em vários lugares e seus seguidores provavelmente serão diferentes de seus diferentes controladores. Você pode definir o segue diretamente do controlador de exibição de tabela para o próximo controlador e executá-lo no código. .

Uma outra observação é que ter o seu celular como um arquivo xib separado impede que você conecte quaisquer ações, etc. diretamente ao controlador de visualização de tabela (eu não trabalhei nisso, de qualquer forma - você não pode definir o proprietário do arquivo como algo significativo ). Eu estou trabalhando em torno disso, definindo um protocolo que espera que o controlador de exibição de tabela da célula esteja em conformidade e adicionando o controlador como uma propriedade fraca, semelhante a um delegado, em cellForRowAtIndexPath.


Eu estava procurando por isso e encontrei esta resposta por Richard Venable. Funciona para mim.

O iOS 5 inclui um novo método no UITableView: registerNib: forCellReuseIdentifier:

Para usá-lo, coloque um UITableViewCell em um bico. Tem que ser o único objeto raiz no bico.

Você pode registrar o bico depois de carregar o seu tableView e, em seguida, quando você chamar dequeueReusableCellWithIdentifier: com o identificador de célula, ele irá puxá-lo da ponta, como se você tivesse usado uma célula de protótipo do Storyboard.


Pelo que entendi, você quer:

  1. Projete uma célula no IB que possa ser usada em várias cenas do storyboard.
  2. O storyboard exclusivo é inserido a partir dessa célula, dependendo da cena em que a célula está.

Infelizmente, não há atualmente nenhuma maneira de fazer isso. Para entender por que suas tentativas anteriores não funcionaram, você precisa entender mais sobre como os storyboards e as células de visualização de tabela de protótipos funcionam. (Se você não se importa com o motivo pelo qual essas outras tentativas não funcionaram, sinta-se à vontade para sair agora. Não tenho soluções mágicas para você, além de sugerir que você registre um bug.)

Um storyboard é, em essência, não muito mais que uma coleção de arquivos .xib. Quando você carrega um controlador de visualização de tabela que tem algumas células protótipo de um storyboard, eis o que acontece:

  • Cada célula protótipo é, na verdade, sua própria mini-ponta incorporada. Portanto, quando o controlador de visualização de tabela está sendo carregado, ele é executado em cada um dos nibs e chamadas da célula do protótipo -[UITableView registerNib:forCellReuseIdentifier:] .
  • A exibição de tabela solicita ao controlador as células.
  • Você provavelmente chamará -[UITableView dequeueReusableCellWithIdentifier:]
  • Quando você solicita uma célula com um dado identificador de reutilização, ele verifica se ela tem uma ponta registrada. Em caso afirmativo, instancia uma instância dessa célula. Isso é composto das seguintes etapas:

    1. Observe a classe da célula, conforme definido na ponta da célula. Chame [[CellClass alloc] initWithCoder:] .
    2. The -initWithCoder: método percorre e adiciona subviews e define propriedades que foram definidas na ponta. (O IBOutlet provavelmente fica ligado aqui também, embora eu não tenha testado isso; isso pode acontecer no -awakeFromNib )
  • Você configura sua célula como quiser.

O importante a notar aqui é que há uma distinção entre a classe da célula e a aparência visual da célula. Você poderia criar duas células protótipo separadas da mesma classe, mas com suas subvisualizações definidas de maneira completamente diferente. Na verdade, se você usar os estilos UITableViewCell padrão, isso é exatamente o que está acontecendo. O estilo "Default" e o estilo "Subtitle", por exemplo, são ambos representados pela mesma classe UITableViewCell .

Isso é importante : a classe da célula não possui uma correlação um-para-um com uma hierarquia de visualização específica. A hierarquia da visão é determinada inteiramente pelo que está na célula protótipo que foi registrada com esse controlador específico.

Note, também, que o identificador de reutilização da célula não foi registrado em algum dispensário global de células. O identificador de reutilização é usado apenas no contexto de uma única instância do UITableView .

Dada essa informação, vamos ver o que aconteceu nas suas tentativas acima.

No Controlador # 1, adicionamos uma célula protótipo, definimos a classe para minha subclasse UITableViewCell, definimos o ID de reutilização, adicionamos os rótulos e os conectamos às saídas da classe. No Controlador # 2, incluiu uma célula protótipo vazia, configurou-a para a mesma classe e reutilizou o id como antes. Quando é executado, os rótulos nunca aparecem quando as células são mostradas no Controlador # 2. Funciona bem no controlador # 1.

Isso é esperado. Embora as duas células tivessem a mesma classe, a hierarquia de exibições transmitida para a célula no Controlador 2 era totalmente desprovida de subvisualizações. Então você tem uma célula vazia, que é exatamente o que você coloca no protótipo.

Projetou cada tipo de célula em um NIB diferente e conectou-se à classe de célula apropriada. No storyboard, adicionamos uma célula protótipo vazia e definimos sua classe e reutilizamos id para nos referirmos à minha classe de célula. Nos métodos viewDidLoad dos controladores, registrou esses arquivos NIB para o ID de reutilização. Quando mostrado, as células em ambos os controladores estavam vazias como o protótipo.

Mais uma vez, isso é esperado. O identificador de reutilização não é compartilhado entre cenas de storyboard ou nibs, portanto, o fato de todas essas células distintas terem o mesmo identificador de reutilização não tinha sentido. A célula que você recebe da tableview terá uma aparência que corresponde à célula protótipo na cena do storyboard.

Essa solução estava próxima, no entanto. Como você observou, você poderia simplesmente chamar programaticamente -[UITableView registerNib:forCellReuseIdentifier:] , passando o UINib contendo a célula, e você retornaria a mesma célula. (Isso não é porque o protótipo estava "substituindo" a ponta; você simplesmente não tinha registrado o bico com a tableview, então ainda estava olhando para a ponta embutida no storyboard). Infelizmente, há uma falha com essa abordagem - não há como ligar os seguidos do storyboard a uma célula em uma ponta autônoma.

Mantive protótipos em ambos os controladores vazios e defini classes e reutilizei id para minha classe de célula. Construiu a interface do usuário das células inteiramente em código. As células funcionam perfeitamente em todos os controladores.

Naturalmente. Espero que isso não seja surpreendente.

Então, é por isso que não funcionou. Você pode projetar suas células em pontas autônomas e usá-las em várias cenas de storyboard; você apenas não pode ligar os seguimentos do storyboard para essas células. Espero que você tenha aprendido alguma coisa no processo de ler isso.







storyboard