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 ...

DrGary
fonte
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
}
Bentford
fonte
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;
}
Giuseppe
fonte
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.

lata
fonte
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"];
vilcsak
fonte
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.
funroll
fonte
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;
}
webstersx
fonte
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.

Alex R. Young
fonte
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

Can Berk Güder
fonte
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.

Shawn Craver
fonte
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; 
    }
Hamiz Ahmed
fonte
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;
}
David H
fonte
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

Geri Borbás
fonte
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;
}
Cameron Lowell Palmer
fonte
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)

Ryan Townshend
fonte
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()

William Hu
fonte
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;
}
Mohit
fonte
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
    }

}
iOS
fonte
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.

MonsieurDart
fonte
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
}
Vadim Bulavin
fonte
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.

Stephan Burlot
fonte
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;
2014
fonte
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")
neoneye
fonte
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

}
Hitesh Chauhan
fonte