Quando utilizzare dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath


167

Ci sono due sovraccarichi per dequeueReusableCellWithIdentifier e sto cercando di determinare quando dovrei usare l'uno contro l'altro?

I documenti apple relativi alla funzione forIndexPath indicano "Questo metodo utilizza il percorso dell'indice per eseguire una configurazione aggiuntiva in base alla posizione della cella nella vista tabella".

Non sono sicuro di come interpretarlo?

Risposte:


216

La differenza più importante è che la forIndexPath:versione afferma (arresti anomali) se non hai registrato una classe o un pennino per l'identificatore. La versione precedente (non forIndexPath:) ritorna nilin quel caso.

Si registra una classe per un identificatore inviando registerClass:forCellReuseIdentifier:alla vista tabella. Registrare un pennino per un identificatore inviando registerNib:forCellReuseIdentifier:alla vista tabella.

Se crei la visualizzazione della tabella e i prototipi delle celle in uno storyboard, il caricatore dello storyboard si occupa della registrazione dei prototipi di celle definiti nello storyboard.

Sessione 200 - Le novità di Cocoa Touch del WWDC 2012 illustrano la (allora nuova) forIndexPath:versione che inizia intorno alle 8.30. Dice che "otterrai sempre una cella inizializzata" (senza menzionare che andrà in crash se non hai registrato una classe o un pennino).

Il video dice anche che "avrà le dimensioni giuste per quel percorso dell'indice". Presumibilmente ciò significa che imposterà la dimensione della cella prima di restituirla, osservando la larghezza della vista tabella e chiamando il tableView:heightForRowAtIndexPath:metodo del delegato (se definito). Questo è il motivo per cui ha bisogno del percorso dell'indice.


È davvero utile, grazie. Avere la dimensione della cella al momento del dequeue sembra meno un vantaggio con il dimensionamento automatico e i vincoli di layout?
Benjohn,

38

dequeueReusableCellWithIdentifier:forIndexPath:sarà sempre tornare una cella. Utilizza ri le celle esistenti o ne crea una nuova e ritorna se non ci sono celle.

Mentre, il tradizionale dequeueReusableCellWithIdentifier:restituirà una cella se esiste, cioè se c'è una cella che può essere riutilizzata restituisce che altrimenti restituisce zero. Quindi dovresti scrivere una condizione per verificare anche il nilvalore.

Per rispondere alla tua domanda usa dequeueReusableCellWithIdentifier:quando vuoi supportare iOS 5 e versioni precedenti poiché dequeueReusableCellWithIdentifier:forIndexPathè disponibile solo su iOS 6+

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


No, non restituisce sempre una cella 26-12-2014 07: 56: 39.947 testProg [4024: 42920390] *** Errore di asserzione in - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 Interphase [4024: 42920390] *** Terminazione dell'app a causa di un'eccezione non rilevata "NSInternalInconsistencyException", motivo: "impossibile dequequare una cella con identificatore MyCustomCellIdentifier - deve registrare un pennino o una classe per l'identificatore o collegare una cellula prototipo in uno storyboard "
chiarisci il

@binarystar È necessario registrare un pennino o una classe della cella personalizzata in vista del caricamento. come:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d,

6

Non ho mai capito perché Apple abbia creato il metodo più recente, dequeueReusableCellWithIdentifier: forIndexPath :. La loro documentazione su di essi non è completa ed è in qualche modo fuorviante. L'unica differenza che sono stato in grado di discernere tra i due metodi, è che quel metodo più vecchio può restituire zero, se non trova una cella con l'identificatore passato, mentre il metodo più recente si arresta in modo anomalo, se non può restituire una cellula. Entrambi i metodi garantiscono la restituzione di una cella, se l'identificatore è stato impostato correttamente e la cella viene creata in uno storyboard. Entrambi i metodi garantiscono inoltre la restituzione di una cella se si registra una classe o xib e si rende la cella in codice o in un file xib.


3
Il nuovo metodo utilizza il percorso dell'indice per determinare la dimensione corretta per la cella.
Rob Mayoff,

1
@robmayoff Ma ha senso? Senza il nuovo metodo, la dimensione della cella può essere impostata correttamente. Il nuovo metodo può offrire qualche comodità?
fujianjin6471,

1
Leggi l'ultimo paragrafo della mia risposta per i dettagli.
Rob Mayoff,

Quindi questo significa che, se tutte le mie celle hanno le stesse dimensioni nella tabella, non importa quale metodo chiamo?
Happiehappie

2
Se fornisco tableView.estimateHeight, anche le dimensioni della cella saranno determinate correttamente. Continuo a non beneficiare del nuovo metodo.
Ryan,

1

In breve:

dequeueReusableCell(withIdentifier, for)funziona solo con celle prototipo. Se provassi a usarlo quando la cella del prototipo è assente, si bloccherebbe l'app.

Hollemans M. 2016, Chapter 2 Checklist, IOS Apprentice (5th Edition). pp: 156.


-2

Consiglierei di utilizzare entrambi se si utilizzano contenuti generati dinamici. In caso contrario, l'app potrebbe bloccarsi in modo imprevisto. È possibile implementare la propria funzione per recuperare una cella riutilizzabile opzionale. In questo caso nildovresti restituire una cella vuota che non è visibile:

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 l'estensione per restituire una cella vuota:

// 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 esempio completo di come 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()
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.