Quando usar dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath

167

Existem duas sobrecargas para o dequeueReusableCellWithIdentifier e estou tentando determinar quando devo usar um vs o outro?

Os documentos da apple sobre a função forIndexPath afirma: "Este método usa o caminho do índice para executar configurações adicionais com base na posição da célula na exibição da tabela".

Eu não tenho certeza de como interpretar isso?

Jaja Harris
fonte

Respostas:

216

A diferença mais importante é que a forIndexPath:versão declara (trava) se você não registrou uma classe ou ponta para o identificador. A forIndexPath:versão mais antiga (não ) retorna nilnesse caso.

Você registra uma classe para um identificador enviando registerClass:forCellReuseIdentifier:para a exibição de tabela. Você registra uma ponta para um identificador enviando registerNib:forCellReuseIdentifier:para a visualização de tabela.

Se você criar sua exibição de tabela e seus protótipos de célula em um storyboard, o carregador de storyboard cuidará do registro dos protótipos de célula que você definiu no storyboard.

Sessão 200 - O que há de novo no Cocoa Touch da WWDC 2012 discute a forIndexPath:versão (então nova) a partir dos 8m30s. Ele diz que “você sempre obterá uma célula inicializada” (sem mencionar que ela travará se você não registrar uma classe ou ponta).

O vídeo também diz que "será do tamanho certo para esse caminho de índice". Presumivelmente, isso significa que ele definirá o tamanho da célula antes de devolvê-la, examinando a largura da visualização da tabela e chamando o tableView:heightForRowAtIndexPath:método do seu representante (se definido). É por isso que ele precisa do caminho do índice.

Rob Mayoff
fonte
Isso é realmente útil, obrigado. Ter o tamanho da célula no tempo de desenfileiramento parece menos uma vantagem com as restrições de dimensionamento e layout automáticas?
precisa saber é
38

dequeueReusableCellWithIdentifier:forIndexPath:vai sempre retornar uma célula. Ele reutiliza as células existentes ou cria uma nova e retorna se não houver células.

Enquanto, o tradicional dequeueReusableCellWithIdentifier:retornará uma célula, se existir, ou seja, se houver uma célula que possa ser reutilizada, ela retornará e, caso contrário, ela retornará nula. Então você teria que escrever uma condição para verificar o nilvalor também.

Para responder à sua pergunta, use dequeueReusableCellWithIdentifier:quando desejar oferecer suporte ao iOS 5 e versões inferiores, pois ele dequeueReusableCellWithIdentifier:forIndexPathestá disponível apenas no iOS 6 ou superior

Referência: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:forIndexPath :

GoodSp33d
fonte
Não, ele nem sempre retorna uma célula 2014-12-26 07: 56: 39.947 testProg [4024: 42920390] *** Falha na asserção em - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 Interfase [4024: 42920390] *** Finalizando o aplicativo devido à exceção não capturada 'NSInternalInconsistencyException', motivo: 'não é possível remover a fila de uma célula com o identificador MyCustomCellIdentifier - deve registrar uma ponta ou uma classe para o identificador ou conectar uma célula protótipo em um storyboard '
clearlight
@binarystar Você deve registrar uma ponta ou classe de sua célula personalizada para que o carregamento tenha sido carregado. como:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d
6

Eu nunca entendi por que a Apple criou o método mais recente, dequeueReusableCellWithIdentifier: forIndexPath :. A documentação deles não está completa e é um tanto enganadora. A única diferença que eu consegui discernir entre os dois métodos é que esse método mais antigo pode retornar nulo, se não encontrar uma célula com o identificador passado, enquanto o método mais novo trava, se não puder retornar. uma célula. É garantido que ambos os métodos retornam uma célula, se você definiu o identificador corretamente, e transformam a célula em um storyboard. Também é garantido que ambos os métodos retornem uma célula se você registrar uma classe ou xib e criar sua célula no código ou em um arquivo xib.

rdelmar
fonte
3
O novo método usa o caminho do índice para determinar o tamanho adequado para a célula.
Rob mayoff
1
@robmayoff Mas isso tem algum sentido? Sem o novo método, o tamanho da célula ainda pode ser definido corretamente. O novo método pode oferecer alguma conveniência?
Fujianjin6471
1
Leia o último parágrafo da minha resposta para obter detalhes.
22615 Robinson May
Então, isso significa que, se todas as minhas células tiverem o mesmo tamanho na tabela, não importa qual método eu chamo?
21416 Happiehappie
2
Se eu fornecer tableView.estimateHeight, o tamanho da célula também será determinado adequadamente. Ainda não recebo o benefício do novo método.
Ryan
1

Como diminutivo:

dequeueReusableCell(withIdentifier, for)só funciona com células protótipo. Se você tentar usá-lo quando a célula protótipo estiver ausente, ele travará o aplicativo.

Hollemans M. 2016, Capítulo 2 Checklist, IOS Apprentice (5ª Edição). pp: 156.

SLN
fonte
-2

Eu recomendaria usar os dois se você estiver usando conteúdo gerado dinamicamente. Caso contrário, seu aplicativo poderá falhar inesperadamente. Você pode implementar sua própria função para recuperar uma célula reutilizável opcional. Se for, nilvocê deve retornar uma célula vazia que não é visível:

Swift 3

// Extensions to UITableView
extension UITableView
{
    // returns nil, if identifier does not exist. 
    // Otherwise it returns a configured cell for the given index path
    open func tryDequeueReusableCell (
        withIdentifier identifier: String, 
        for indexPath: IndexPath) -> UITableViewCell?
    {
        let cell = self.dequeueReusableCell(withIdentifier: identifier)
        if cell != nil {
            return self.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
        }  
        return nil
    }
}

E a extensão para retornar uma célula vazia:

// Extension to UITableViewCell
extension UITableViewCell
{
    // Generates an empty table cell that is not visible
    class func empty() -> UITableViewCell
    {
        let emptyCell = UITableViewCell(frame:CGRect(x:0, y:0, width:0, height:0))
        emptyCell.backgroundColor = UIColor.clear
        return emptyCell
    }
}

Um exemplo completo de como usá-lo:

import Foundation
import UIKit

// A protocol is used to identify if we can configure
// a cell with CellData
protocol ConfigureAbleWithCellData
{
    func configure(_ data: CellData)
}

class MyCustomTableViewCell :
    UITableViewCell,
    ConfigureAbleWithCellData
{
    @IBOutlet weak var title:UILabel! = nil
    func configure(_ data: CellData)
    {
        self.title.text = data.title
    }
}

// This actually holds the data for one cell
struct CellData
{
    var title:String = ""
    var reusableId:String = ""
}

class CosmoConverterUnitTableViewController:
    UIViewController,
    UITableViewDelegate,
    UITableViewDataSource
{
    // Storage
    var data = Array<Array<CellData>>()

    func loadData()
    {
        var section1:[CellData] = []
        var section2:[CellData] = []

        section1.append(CellData(title:"Foo", reusableId:"cellType1"))
        section2.append(CellData(title:"Bar", reusableId:"cellType2"))

        data.append(section1)
        data.append(section2)
    }

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

    public func numberOfSections(in tableView: UITableView) -> Int
    {
        return data.count
    }

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

        guard
            indexPath.row < data[indexPath.section].count
            else
        {
            fatalError("this can't be")
        }

        let cellData = data[indexPath.section][indexPath.row]

        if let cell = tableView.tryDequeueReusableCell(
            withIdentifier: cellData.reusableId,
            for: indexPath)
        {
            if let configurableCell = cell as? ConfigureAbleWithCellData
            {
                configurableCell.configure(cellData)
            }
            else
            {
                // cell is not of type ConfigureAbleWithCellData
                // so we cant configure it.
            }
            return cell
        }
        // id does not exist
        return UITableViewCell.empty()
    }
}
hhamm
fonte