make - xcode ios framework tutorial




Como você carrega UITableViewCells personalizados de arquivos Xib? (16)

A questão é simples: como você carrega o UITableViewCell personalizado de arquivos 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 é pré-NDA-release 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 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 UITableViewCell e adicione IBOutlets para os componentes desejados. Em seguida, crie um novo arquivo "Empty XIB". Abra o arquivo Xib no IB, adicione um objeto UITableViewCell , defina seu identificador como "MyCellIdentifier" e defina sua classe como MyCell e adicione seus componentes. Finalmente, conecte os IBOutlets aos 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. Eu 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? Precisamos de uma fábrica? Se sim, como é o código da fábrica? Se existem várias soluções, vamos esclarecer os prós e contras de cada um deles ...


registo

Após o iOS 7, este 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 .stroyboard ou .stroyboard , como células protótipo. Se você precisar anexar uma classe a eles, você pode selecionar o protótipo da célula e adicionar a classe correspondente (deve ser um descendente do UITableViewCell , é claro).

Dequeue

E mais tarde, dequeued 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 este novo método não só extrai a célula, mas também cria se não existir (isso significa que você não precisa fazer if (cell == nil) shenanigans), e a célula está pronta para usar da mesma forma que no exemplo acima.

( Atenção ) tableView.dequeueReusableCell(withIdentifier:for:) tem o novo comportamento, se você chamar o outro (sem indexPath: você obtém o comportamento antigo, no qual você precisa verificar por nil e por instância você mesmo, 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 da classe associada da célula é aquele que você definiu no arquivo .xib para a subclasse UITableViewCell 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 de conteúdo (como rótulos e visualizações de imagens) no momento em que você as registrou, e no método cellForRowAtIndexPath 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 na ObjC com os mesmos nomes.


  1. Crie sua própria classe personalizada Subclasse AbcViewCell de UITableViewCell (Certifique-se de que o nome do arquivo da classe e o nome do arquivo da ponta sejam 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()


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

Aqui está o método de classe que 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 visualização em vez do

[[UITableViewCell] alloc] initWithFrame:]

É bastante rápido o suficiente e usado em dois dos meus aplicativos de remessa. É mais confiável do que chamar [nib objectAtIndex:0] , e na minha opinião, pelo menos, mais confiável do que o exemplo de Stephan Burlot, porque você tem a garantia de obter apenas uma visualização de um XIB que seja do tipo certo.


Aqui está uma abordagem universal para registrar células no 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. Reusable protocolo Reusable 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. UITableViewCell está em conformidade com o protocolo Reusable .
  3. UITableView extensão UITableView abstrai a diferença no registro de células via bico 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
}

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

Veja a postagem atual para mais detalhes. Eu prefiro o método # 2 como 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. Ela costumava estar nos documentos oficiais, mas agora é removida em favor de storyboards.

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


Eu decidi postar desde que eu não gosto de nenhuma dessas respostas - as coisas sempre podem ser mais simples e esta é de longe a maneira mais concisa que eu encontrei.

1. Construa seu Xib no Interface Builder como você gosta

  • Defina o proprietário do arquivo para a classe NSObject
  • Adicione um UITableViewCell e defina sua classe para MyTableViewCellSubclass - se o seu IB travar (acontece no Xcode> 4 até o momento da redação deste texto), basta usar um UIView para fazer a interface no Xcode 4 se você ainda tiver
  • Elabore suas subvisualizações dentro desta célula e anexe suas conexões IBOutlet à sua @interface no .h ou .m (.m é minha preferência)

2. Na sua 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 MyTableViewCellSubclass

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

    return self;
}

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

  • Criar um xib para um ViewController
  • Definir a classe do proprietário do arquivo para UIViewController
  • Exclua a visualização e adicione um UITableViewCell
  • Defina a classe do seu UITableViewCell para sua classe personalizada
  • Definir o identificador do seu UITableViewCell
  • Defina a saída da visualização do controlador de exibição para o seu 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.


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

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


No Swift 4.2 e no Xcode 10

Eu tenho três arquivos de célula XIB

em ViewDidLoad registre seus arquivos XIB como este ...

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 cellPortRowAt indexPath:

Esta é a minha função de delegação 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
    }

}

O que eu faço para isso é declarar uma IBOutlet UITableViewCell *cell em sua classe controladora. Em seguida, invoque o método de classe NSBundle loadNibNamed , que alimentará o UITableViewCell para a célula declarada acima.

Para o xib, vou criar um xib vazio e adicionar o objeto UITableViewCell no IB, onde ele pode ser configurado conforme necessário. Essa visão é então conectada ao IBOutlet da célula na 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 NSBundle loadNibNamed (ADC login)

Artigo do cocoawithlove.com Eu obtive o conceito de (pegue o aplicativo de amostra de números de telefone)


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

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

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

Eu acidentalmente esqueci de definir alguns identificadores em um projeto com muita mesa, e a mudança de desempenho foi como dia e noite.


Solução Correta é isso

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

 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;

 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

}




xib