Como você carrega UITableViewCells personalizado a partir de arquivos Xib?


293

A pergunta é simples: como você carrega UITableViewCellarquivos personalizados do Xib? Isso permite que você use o Interface Builder para projetar suas células. A resposta aparentemente não é simples devido a problemas de gerenciamento de memória. Este tópico menciona o problema e sugere uma solução, mas é anterior ao lançamento do NDA e não possui código. Aqui está um longo tópico que discute o problema sem fornecer uma resposta definitiva.

Aqui está um código que eu usei:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

Para usar esse código, crie MyCell.m / .h, uma nova subclasse de UITableViewCelle adicione IBOutletsos componentes desejados. Em seguida, crie um novo arquivo "Empty XIB". Abra o arquivo Xib no IB, adicione um UITableViewCellobjeto, defina seu identificador como "MyCellIdentifier" e defina sua classe como MyCell e adicione seus componentes. Por fim, conecte-o IBOutletsaos componentes. Observe que não definimos o proprietário do arquivo no IB.

Outros métodos defendem a configuração do Proprietário do arquivo e avisam sobre vazamentos de memória se o Xib não for carregado por meio de uma classe de fábrica adicional. Testei o acima em Instrumentos / Vazamentos e não vi vazamentos de memória.

Então, qual é a maneira canônica de carregar células do Xibs? Nós definimos o proprietário do arquivo? Nós precisamos de uma fábrica? Em caso afirmativo, como é o código da fábrica? Se houver várias soluções, vamos esclarecer os prós e contras de cada uma delas ...


2
Alguém pode editar o assunto para realmente fazer a pergunta, ou seja, "Como você carrega UITableViewCells personalizados de arquivos Xib?" (Ignorar se isso simplesmente não é possível em stackoverflow.)
— Steven Fisher

1
Para o iOS 5 e posterior, esta é a solução: stackoverflow.com/questions/15591364/… , que é a mesma que a solução da giuseppe.
— Matt Becker

Nota rápida, resposta mais simples (ambiente de 2013) aqui stackoverflow.com/questions/15378788/… jamihash
— Fattie

Respostas:


288

Aqui estão dois métodos que o autor original declara ter sido recomendado por um engenheiro do IB .

Veja a publicação real para mais detalhes. Eu prefiro o método 2, pois parece mais simples.

Método 1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

Método 2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Atualização (2014): O método 2 ainda é válido, mas não há mais documentação para ele. Costumava estar nos documentos oficiais mas agora é removido em favor dos storyboards.

Publiquei um exemplo de trabalho no Github:
https://github.com/bentford/NibTableCellExample

editar para Swift 4.2

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}

1
Para o método 1, você não deve fazer algo como "cell = (BDCustomCell *) [[temporaryController.view reter] autorelease];" então a célula não é liberada quando o controlador temporário é liberado?
— Tod Cunningham

Hum. A documentação que fala sobre o número 2 ainda diz para você definir o proprietário da célula no arquivo XIB, para uma classe de controlador conhecida. Talvez não importe quando você define o proprietário durante o carregamento.
— Oscar

@OscarGoldman O proprietário da célula no arquivo XIB é uma classe (ou seja, o tipo de proprietário.) O proprietário da célula em loadNibNamed: owner: options: é um objeto do tipo especificado no XIB.
— bentford

2
A opção #CoolDocMan # 2 ainda funciona. O problema é mais provável com a ponta. Aqui está um exemplo: github.com/bentford/NibTableCellExample
— bentford

2
Por que esse código super antigo é tão alto? Stackoverflow do alguma coisa: /
— Nico S.

304

A solução certa é esta:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}

isso vai quebrar os aplicativos iOS5? Eu realmente nunca vi UINib
— Adam Waite

@AdamWaite O registro de arquivos NIB funciona no iOS 5 e posterior, portanto não está quebrando os aplicativos do iOS 5. E UINib mesmo existe desde iOS 4.
— Mecki

Para um bom exemplo, verifique o
— repositório

39

Registro

Após o iOS 7, esse processo foi simplificado para ( swift 3.0 ):

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Nota ) Isso também é possível criando as células nos arquivos .xibou .stroyboard, como células protótipo. Se você precisar anexar uma classe a eles, poderá selecionar o protótipo da célula e adicionar a classe correspondente (é UITableViewCellclaro que deve ser descendente ).

Retirar da fila

E mais tarde, desenfileirado usando ( swift 3.0 ):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

A diferença é que esse novo método não apenas remove a fila da célula, mas também cria se não existe (isso significa que você não precisa fazer if (cell == nil)travessuras), e a célula está pronta para uso, como no exemplo acima.

( Aviso ) tableView.dequeueReusableCell(withIdentifier:for:)tem o novo comportamento; se você chamar o outro (sem indexPath:), obtém o antigo comportamento, no qual você deve verificar nile instanciar, observe o UITableViewCell?valor de retorno.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

E, é claro, o tipo de classe associada da célula é o que você definiu no arquivo .xib para a UITableViewCellsubclasse ou, alternativamente, usando o outro método de registro.

Configuração

Idealmente, suas células já foram configuradas em termos de aparência e posicionamento do conteúdo (como rótulos e visualizações de imagens) quando você as registrou e no cellForRowAtIndexPathmétodo que você simplesmente as preenche.

Todos juntos

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

E, claro, tudo isso está disponível no ObjC com os mesmos nomes.


Aqui está a versão do objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
— Zeb

33

Tomou a resposta de Shawn Craver e a limpou um pouco.

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

Faço todas as subclasses de BBCell do meu UITableViewCell e substituo o padrão

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

com:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];

16

Eu usei o método # 2 de Bentford :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Funciona, mas cuidado com as conexões com o Proprietário do arquivo no seu arquivo .xib UITableViewCell personalizado.

Ao passar owner:selfseu loadNibNamedextrato, você define UITableViewControllercomo o proprietário do arquivo UITableViewCell.

Se você arrastar e soltar no arquivo de cabeçalho no IB para configurar ações e saídas, ele será configurado como Proprietário do arquivo por padrão.

Em loadNibNamed:owner:options, o código da Apple tentará definir propriedades no seu UITableViewController, já que esse é o proprietário. Mas como você não possui essas propriedades definidas, obtém um erro ao ser compatível com a codificação do valor-chave :

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

Se um evento for acionado, você receberá uma NSInvalidArgumentException:

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

Uma solução fácil é apontar as conexões do Interface Builder para o UITableViewCellproprietário do arquivo, em vez do:

  1. Clique com o botão direito no proprietário do arquivo para abrir a lista de conexões
  2. Faça uma captura de tela com Command-Shift-4 (arraste para selecionar a área a ser capturada)
  3. x as conexões do proprietário do arquivo
  4. Clique com o botão direito do mouse em UITableCell na hierarquia de objetos e adicione novamente as conexões.

Eu tive o problema que você mencionou, mas como apontar as conexões para o UITableViewCell em vez do proprietário do arquivo? Não entendo seus passos, por exemplo, por que é necessária uma captura de tela? e quando eu cliquei no botão Adicionar próximo à saída, nada acontece
— xu huanze

@xuhuanze Sugeri fazer uma captura de tela para que você tenha um registro de quais coisas o proprietário do arquivo já estava conectado. Em seguida, você pode recriar as mesmas conexões. Você precisa arrastar e soltar para adicionar as conexões - não apenas um clique.
— funroll

Muito obrigado, tive o problema "esta classe não é compatível com a codificação de valores-chave para a chave" e o resolvi com sua ajuda. Quero dizer aos outros que você também deve alterar uma classe do seu UITableViewCell para sua classe, que você usa como uma classe de célula personalizada.
— Denis Kutlubaev

14

Decidi postar porque não gosto de nenhuma dessas respostas - as coisas sempre podem ser mais simples e essa é de longe a maneira mais concisa que encontrei.

1. Construa seu Xib no Interface Builder como quiser

  • Defina o proprietário do arquivo para a classe NSObject
  • Adicione um UITableViewCell e defina sua classe como MyTableViewCellSubclass - se o seu IB travar (acontece no Xcode> 4 até o momento em que este artigo foi escrito), basta usar um UIView de fazer a interface no Xcode 4, se você ainda o tiver
  • Faça o layout de suas subvisões dentro desta célula e anexe suas conexões IBOutlet à sua interface @ no .h ou .m (.m é a minha preferência)

2. Na subclasse UIViewController ou UITableViewController

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. Na sua classe MyTableViewCellSubclass

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}

9

Se você estiver usando o Interface Builder para criar células, verifique se configurou o Identificador no Inspetor. Em seguida, verifique se é o mesmo ao chamar dequeueReusableCellWithIdentifier.

Esqueci-me acidentalmente de definir alguns identificadores em um projeto pesado de tabelas, e a mudança de desempenho foi como dia e noite.


8

Carregar UITableViewCells de XIBs economiza muito código, mas geralmente resulta em velocidade de rolagem horrível (na verdade, não é o XIB, mas o uso excessivo de UIViews que causa isso).

Eu sugiro que você dê uma olhada nisso: Referência de link


6

Aqui está o método de classe que eu tenho usado para criar células personalizadas a partir de XIBs:

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

Em seguida, no XIB, defino o nome da classe e reutilizo o identificador. Depois disso, posso chamar esse método no meu controlador de exibição em vez do

[[UITableViewCell] alloc] initWithFrame:]

É rápido o suficiente e está sendo usado em dois dos meus aplicativos de remessa. É mais confiável do que ligar [nib objectAtIndex:0]e, pelo menos em minha opinião, mais confiável do que o exemplo de Stephan Burlot, porque você garante apenas uma visualização de um XIB do tipo certo.


5

Solução correta é esta

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    return cell; 
    }

4

Recarregar o NIB é caro. Melhor carregá-lo uma vez e instanciar os objetos quando precisar de uma célula. Observe que você pode adicionar UIImageViews etc. à ponta, mesmo várias células, usando este método (o iOS5 "registerNIB" da Apple permite apenas um objeto de nível superior - Bug 10580062 "iOS5 tableView registerNib: excessivamente restritivo"

Então, meu código está abaixo - você lê o NIB uma vez (na inicialização como eu fiz ou no viewDidload - seja o que for. A partir de então, você instancia a ponta em objetos e escolhe o que você precisa. Isso é muito mais eficiente do que carregar a ponta de novo e de novo.

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"TheCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}

4

Verifique isto - http://eppz.eu/blog/custom-uitableview-cell/ - maneira realmente conveniente usando uma classe minúscula que termina uma linha na implementação do controlador:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

insira a descrição da imagem aqui


3

A maneira correta de fazer isso é criar uma implementação, cabeçalho e XIB da subclasse UITableViewCell. No XIB, remova todas as visualizações e adicione uma célula da tabela. Defina a classe como o nome da subclasse UITableViewCell. Para o proprietário do arquivo, torne-o o nome da classe da subclasse UITableViewController. Conecte o proprietário do arquivo à célula usando a tomada tableViewCell.

No arquivo de cabeçalho:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

No arquivo de implementação:

@synthesize tableViewCell = _tableViewCell;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"reusableCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}

3

O que faço para isso é declarar um IBOutlet UITableViewCell *cellna sua classe de controlador. Em seguida, chame o NSBundle loadNibNamedmétodo de classe, que alimentará oUITableViewCell a célula declarada acima.

Para o xib, criarei um xib vazio e adicionei o UITableViewCellobjeto no IB, onde ele pode ser configurado conforme necessário. Essa visualização é então conectada à célula IBOutletna classe do controlador.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

Adições ao NSBundle loadNibNamed (login no ADC)

artigo de cocoawithlove.com de onde parti do conceito (obtenha o aplicativo de exemplo de números de telefone)


3
  1. Crie sua própria AbcViewCellsubclasse de classe personalizada em UITableViewCell(Verifique se o nome do arquivo da classe e o nome do arquivo da ponta são os mesmos)

  2. Crie este método de classe de extensão.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. Use-o.

    let cell: AbcViewCell = UITableViewCell.fromNib()


2

Primeiro importe seu arquivo de célula personalizado #import "CustomCell.h"e, em seguida, altere o método delegate, conforme mencionado abaixo:

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

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

     return cell;
}

2

No Swift 4.2 e no Xcode 10

Eu tenho três arquivos de célula XIB

no ViewDidLoad registre seus arquivos XIB assim ...

Esta é a primeira abordagem

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

A segunda abordagem registra diretamente os arquivos XIB no cellForRowAt indexPath:

Esta é a minha função de delegar tableview

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}

1

Aqui está o meu método para isso: Carregando UITableViewCells personalizados a partir de arquivos XIB ... Outro método

A idéia é criar uma subclasse SampleCell UITableViewCellcom uma IBOutlet UIView *contentpropriedade e uma propriedade para cada subvisão personalizada que você precisa configurar a partir do código. Em seguida, crie um arquivo SampleCell.xib. Nesse arquivo de ponta, altere o proprietário do arquivo para SampleCell. Adicione um conteúdo UIViewdimensionado para atender às suas necessidades. Adicione e configure todas as subvisões (etiqueta, visualizações de imagem, botões, etc.) que você deseja. Por fim, vincule a exibição do conteúdo e as subvisões ao proprietário do arquivo.


1

Aqui está uma abordagem universal para registrar células em UITableView:

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Explicação:

  1. ReusableO protocolo gera o ID da célula a partir do nome da classe. Certifique-se de seguir a convenção:cell ID == class name == nib name .
  2. UITableViewCellestá em conformidade com o Reusableprotocolo.
  3. UITableView A extensão abstrai a diferença no registro de células via ponta ou classe.

Exemplo de uso:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}

0

Não sei se existe uma maneira canônica, mas aqui está o meu método:

  • Crie um xib para um ViewController
  • Defina a classe Proprietário do arquivo como UIViewController
  • Exclua a visualização e adicione um UITableViewCell
  • Defina a classe do seu UITableViewCell como sua classe personalizada
  • Defina o identificador do seu UITableViewCell
  • Defina a saída da visualização do controlador de exibição como UITableViewCell

E use este código:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

No seu exemplo, usando

[nib objectAtIndex:0]

pode quebrar se a Apple alterar a ordem dos itens no xib.


Para mim, isso resulta na criação de uma nova instância sempre. o desenfileiramento parece retornar sempre nulo.
— estranho

0
 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];


    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell=nil;

    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[NewsFeedCell class]])
            {
                cell = (NewsFeedCell *)currentObject;
                break;
            }
        }
}
return cell;

0

Esta extensão requer o Xcode7 beta6

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

Crie um arquivo Xib que contenha apenas 1 UITableViewCell personalizado.

Carregue-o.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")

0
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cellReuseIdentifier = "collabCell"
            var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
            if cell == nil {
                tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
            }


            return cell

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.