Cuándo usar dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath

167

Hay dos sobrecargas para dequeueReusableCellWithIdentifier y estoy tratando de determinar cuándo debo usar uno frente al otro.

Los documentos de Apple con respecto a la función forIndexPath dicen: "Este método usa la ruta del índice para realizar una configuración adicional basada en la posición de la celda en la vista de tabla".

Sin embargo, no estoy seguro de cómo interpretar eso.

Jaja Harris
fuente

Respuestas:

216

La diferencia más importante es que la forIndexPath:versión afirma (se bloquea) si no registró una clase o punta para el identificador. La forIndexPath:versión anterior (no ) vuelve nilen ese caso.

Usted registra una clase para un identificador enviando registerClass:forCellReuseIdentifier:a la vista de tabla. Usted registra una punta para un identificador enviando registerNib:forCellReuseIdentifier:a la vista de tabla.

Si crea su vista de tabla y sus prototipos de celda en un guión gráfico, el cargador del guión gráfico se encarga de registrar los prototipos de celda que definió en el guión gráfico.

La sesión 200: Novedades de Cocoa Touch de WWDC 2012 analiza la forIndexPath:versión (entonces nueva) que comienza alrededor de los 8m30. Dice que "siempre obtendrá una celda inicializada" (sin mencionar que se bloqueará si no registró una clase o punta).

El video también dice que "será el tamaño correcto para esa ruta de índice". Presumiblemente, esto significa que establecerá el tamaño de la celda antes de devolverla, observando el ancho de la vista de la tabla y llamando al tableView:heightForRowAtIndexPath:método de su delegado (si está definido). Es por eso que necesita la ruta del índice.

Rob Mayoff
fuente
Eso es realmente útil, gracias. ¿Tener el tamaño de celda en tiempo de espera parece menos ventajoso con las restricciones de tamaño y diseño automático?
Benjohn
38

dequeueReusableCellWithIdentifier:forIndexPath:será siempre volver una célula. Reutiliza las celdas existentes o crea una nueva y regresa si no hay celdas.

Mientras que, el tradicional dequeueReusableCellWithIdentifier:devolverá una celda si existe, es decir, si hay una celda que se puede reutilizar, devuelve que, de lo contrario, devuelve nulo. Por lo tanto, también tendría que escribir una condición para verificar el nilvalor.

Para responder a su pregunta, use dequeueReusableCellWithIdentifier:cuando desee admitir iOS 5 y versiones anteriores, ya dequeueReusableCellWithIdentifier:forIndexPathque solo está disponible en iOS 6+

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

GoodSp33d
fuente
No, no siempre devuelve una celda 2014-12-26 07: 56: 39.947 testProg [4024: 42920390] *** Error de aserción en - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 Interphase [4024: 42920390] *** Finalización de la aplicación debido a la excepción no detectada 'NSInternalInconsistencyException', razón: 'no se puede quitar una celda con el identificador MyCustomCellIdentifier - debe registrar una punta o una clase para el identificador o conectar una celda prototipo en un guión gráfico '
clearlight
@binarystar Debe registrar un plumín o clase de su celda personalizada en vista que se cargó. como:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d
6

Nunca he entendido por qué Apple creó el método más nuevo, dequeueReusableCellWithIdentifier: forIndexPath :. Su documentación sobre ellos no está completa y es algo engañosa. La única diferencia que he podido discernir entre los dos métodos es que ese método anterior puede devolver nulo, si no encuentra una celda con el identificador pasado, mientras que el método más nuevo falla, si no puede regresar Una célula. Se garantiza que ambos métodos devuelven una celda, si ha configurado el identificador correctamente, y hacen la celda en un guión gráfico. Ambos métodos también están garantizados para devolver una celda si registra una clase o xib, y convierte su celda en código o en un archivo xib.

rdelmar
fuente
3
El nuevo método utiliza la ruta del índice para determinar el tamaño adecuado para la celda.
rob mayoff
1
@robmayoff ¿Pero esto tiene algún sentido? Sin el nuevo método, el tamaño de la celda todavía se puede configurar correctamente. ¿Puede el nuevo método ofrecer alguna comodidad?
fujianjin6471
1
Lea el último párrafo de mi respuesta para más detalles.
rob mayoff
Entonces, ¿esto significa que, si todas mis celdas son del mismo tamaño en la tabla, no importa a qué método llame?
Happiehappie
2
Si proporciono tableView.estimateHeight, el tamaño de la celda también se determinará correctamente. Todavía no obtengo el beneficio del nuevo método.
Ryan
1

Para abreviar:

dequeueReusableCell(withIdentifier, for)solo funciona con células prototipo. Si trató de usarlo cuando la célula prototipo está ausente, se bloqueará la aplicación.

Hollemans M. 2016, Capítulo 2 Lista de verificación, IOS Apprentice (5th Edition). pp: 156.

SLN
fuente
-2

Recomendaría usar ambos si está utilizando contenido dinámico generado. De lo contrario, su aplicación podría bloquearse inesperadamente. Puede implementar su propia función para recuperar una celda reutilizable opcional. Si esto esnil así, debe devolver una celda vacía que no es visible:

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

Y la extensión para devolver una celda vacía:

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

Un ejemplo completo de cómo usarlo:

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
fuente