Verwendung von dequeueReusableCellWithIdentifier im Vergleich zu dequeueReusableCellWithIdentifier: forIndexPath

167

Es gibt zwei Überladungen für dequeueReusableCellWithIdentifier und ich versuche festzustellen, wann ich eine gegen die andere verwenden soll.

In den Apple-Dokumenten zur Funktion forIndexPath heißt es: "Diese Methode verwendet den Indexpfad , um eine zusätzliche Konfiguration basierend auf der Position der Zelle in der Tabellenansicht durchzuführen."

Ich bin mir nicht sicher, wie ich das interpretieren soll?

Jaja Harris
quelle

Antworten:

216

Der wichtigste Unterschied besteht darin, dass die forIndexPath:Version bestätigt (abstürzt), wenn Sie keine Klasse oder Feder für den Bezeichner registriert haben. Die ältere (Nicht- forIndexPath:) Version wird nilin diesem Fall zurückgegeben.

Sie registrieren eine Klasse für einen Bezeichner, indem Sie sie registerClass:forCellReuseIdentifier:an die Tabellenansicht senden . Sie registrieren eine Schreibfeder für eine Kennung, indem Sie sie registerNib:forCellReuseIdentifier:an die Tabellenansicht senden .

Wenn Sie Ihre Tabellenansicht und Ihre Zellenprototypen in einem Storyboard erstellen, übernimmt der Storyboard-Loader die Registrierung der im Storyboard definierten Zellprototypen.

In Sitzung 200 - Was ist neu in Cocoa Touch von WWDC 2012forIndexPath: ? Wird die (damals neue) Version ab ca. 8:30 Uhr besprochen. Es heißt, dass "Sie immer eine initialisierte Zelle erhalten" (ohne zu erwähnen, dass sie abstürzt, wenn Sie keine Klasse oder Feder registriert haben).

Das Video sagt auch, dass "es die richtige Größe für diesen Indexpfad haben wird". Vermutlich bedeutet dies, dass die Größe der Zelle vor der Rückgabe festgelegt wird, indem die eigene Breite der Tabellenansicht überprüft und die tableView:heightForRowAtIndexPath:Methode Ihres Delegaten aufgerufen wird (falls definiert). Aus diesem Grund benötigt es den Indexpfad.

Rob Mayoff
quelle
Das ist wirklich hilfreich, danke. Die Größe der Zelle zum Zeitpunkt der Warteschlange scheint bei Einschränkungen der automatischen Größe und des Layouts weniger vorteilhaft zu sein?
Benjohn
38

dequeueReusableCellWithIdentifier:forIndexPath:gibt immer eine Zelle zurück. Es verwendet entweder vorhandene Zellen erneut oder erstellt eine neue und kehrt zurück, wenn keine Zellen vorhanden sind.

Während das traditionelle dequeueReusableCellWithIdentifier:System eine Zelle zurückgibt, wenn es existiert, dh wenn es eine Zelle gibt, die wiederverwendet werden kann, gibt es zurück, dass es sonst null zurückgibt. Sie müssten also eine Bedingung schreiben, um auch den nilWert zu überprüfen .

Um Ihre Frage zu beantworten, verwenden dequeueReusableCellWithIdentifier:Sie, wenn Sie iOS 5 und niedrigere Versionen unterstützen möchten, da dies dequeueReusableCellWithIdentifier:forIndexPathnur unter iOS 6+ verfügbar ist

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

GoodSp33d
quelle
Nein, es wird nicht immer eine Zelle zurückgegeben. 2014-12-26 07: 56: 39.947 testProg [4024: 42920390] *** Assertionsfehler in - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 Interphase [4024: 42920390] *** Beenden der App aufgrund der nicht erfassten Ausnahme 'NSInternalInconsistencyException', Grund: 'Eine Zelle mit der Kennung MyCustomCellIdentifier kann nicht aus der Warteschlange entfernt werden - muss eine Schreibfeder registrieren oder eine Klasse für die Kennung oder verbinden Sie eine Prototypzelle in einem Storyboard '
Clearlight
@binarystar Sie müssen eine Schreibfeder oder Klasse Ihrer benutzerdefinierten Zelle registrieren, die in der Ansicht geladen wurde. wie:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d
6

Ich habe nie verstanden, warum Apple die neuere Methode dequeueReusableCellWithIdentifier: forIndexPath: erstellt hat. Ihre Dokumentation ist nicht vollständig und etwas irreführend. Der einzige Unterschied, den ich zwischen den beiden Methoden feststellen konnte, besteht darin, dass diese ältere Methode null zurückgeben kann, wenn sie keine Zelle mit dem übergebenen Bezeichner findet, während die neuere Methode abstürzt, wenn sie nicht zurückkehren kann eine Zelle. Beide Methoden geben garantiert eine Zelle zurück, wenn Sie den Bezeichner richtig eingestellt haben, und erstellen die Zelle in einem Storyboard. Beide Methoden geben garantiert auch eine Zelle zurück, wenn Sie eine Klasse oder xib registrieren und Ihre Zelle in Code oder einer xib-Datei erstellen.

rdelmar
quelle
3
Die neue Methode verwendet den Indexpfad, um die richtige Größe für die Zelle zu bestimmen.
Rob Mayoff
1
@robmayoff Aber hat das irgendeinen Sinn? Ohne die neue Methode kann die Größe der Zelle immer noch richtig eingestellt werden. Kann die neue Methode Komfort bieten?
Fujianjin6471
1
Lesen Sie den letzten Absatz meiner Antwort für Details.
Rob Mayoff
Bedeutet dies also, dass es keine Rolle spielt, welche Methode ich aufrufe, wenn alle meine Zellen in der Tabelle die gleiche Größe haben?
Happiehappie
2
Wenn ich zur Verfügung stelle tableView.estimateHeight, wird auch die Größe der Zelle richtig bestimmt. Ich bekomme immer noch nicht den Vorteil der neuen Methode.
Ryan
1

Kurzum:

dequeueReusableCell(withIdentifier, for)funktioniert nur mit Prototypzellen. Wenn Sie versuchen, es zu verwenden, wenn die Prototypzelle fehlt, stürzt die App ab.

Hollemans M. 2016, Kapitel 2 Checkliste, IOS Apprentice (5. Ausgabe). S. 156.

SLN
quelle
-2

Ich würde empfehlen, beide zu verwenden, wenn Sie dynamisch generierten Inhalt verwenden. Andernfalls kann Ihre App unerwartet abstürzen. Sie können Ihre eigene Funktion implementieren, um eine optionale wiederverwendbare Zelle abzurufen. Wenn dies der Fall ist nil, sollten Sie eine leere Zelle zurückgeben, die nicht sichtbar ist:

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

Und die Erweiterung, um eine leere Zelle zurückzugeben:

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

Ein vollständiges Beispiel für die Verwendung:

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
quelle