Quand utiliser dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier: forIndexPath

167

Il y a deux surcharges pour dequeueReusableCellWithIdentifier et j'essaie de déterminer quand dois-je utiliser l'un par rapport à l'autre?

La documentation Apple concernant la fonction forIndexPath indique: «Cette méthode utilise le chemin d'index pour effectuer une configuration supplémentaire en fonction de la position de la cellule dans la vue de table».

Je ne sais pas trop comment interpréter cela?

Jaja Harris
la source

Réponses:

216

La différence la plus importante est que la forIndexPath:version affirme (plante) si vous n'avez pas enregistré de classe ou de nib pour l'identifiant. L'ancienne forIndexPath:version (non- ) retourne nildans ce cas.

Vous enregistrez une classe pour un identifiant en l'envoyant registerClass:forCellReuseIdentifier:à la vue table. Vous enregistrez une plume pour un identifiant en l'envoyant registerNib:forCellReuseIdentifier:à la vue de table.

Si vous créez votre vue de tableau et vos prototypes de cellule dans un storyboard, le chargeur de storyboard se charge d'enregistrer les prototypes de cellule que vous avez définis dans le storyboard.

Session 200 - Quoi de neuf dans Cocoa Touch de la WWDC 2012 discute de la (alors nouvelle) forIndexPath:version à partir d'environ 8m30s. Il dit que "vous obtiendrez toujours une cellule initialisée" (sans mentionner qu'elle plantera si vous n'avez pas enregistré de classe ou de nib).

La vidéo dit également que "ce sera la bonne taille pour ce chemin d'index". Cela signifie probablement qu'il définira la taille de la cellule avant de la renvoyer, en regardant la largeur de la vue tableau et en appelant la tableView:heightForRowAtIndexPath:méthode de votre délégué (si elle est définie). C'est pourquoi il a besoin du chemin d'index.

Rob Mayoff
la source
C'est vraiment utile, merci. Avoir la cellule dimensionnée au moment du retrait de la file d'attente semble moins un avantage avec le dimensionnement automatique et les contraintes de mise en page?
Benjohn
38

dequeueReusableCellWithIdentifier:forIndexPath:sera toujours retourner une cellule. Il réutilise des cellules existantes ou en crée une nouvelle et retourne s'il n'y a pas de cellules.

Alors que, le traditionnel dequeueReusableCellWithIdentifier:retournera une cellule si elle existe, c'est-à-dire s'il y a une cellule qui peut être réutilisée, il retourne que sinon, il retourne nil. Vous devrez donc également écrire une condition pour vérifier la nilvaleur.

Pour répondre à votre question, utilisez dequeueReusableCellWithIdentifier:lorsque vous souhaitez prendre en charge iOS 5 et les versions inférieures, car dequeueReusableCellWithIdentifier:forIndexPathdisponible uniquement sur iOS 6+

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

GoodSp33d
la source
Non, il ne renvoie pas toujours une cellule 2014-12-26 07: 56: 39.947 testProg [4024: 42920390] *** Échec d'assertion dans - [UITableView dequeueReusableCellWithIdentifier: forIndexPath:], /SourceCache/UIKit_Sim/UIKit-3318.65/ UITableView.m: 6116 2014-12-26 07: 56: 39.954 Interphase [4024: 42920390] *** Arrêt de l'application en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'impossible de retirer une cellule avec l'identifiant MyCustomCellIdentifier - doit enregistrer une pointe ou une classe pour l'identifiant ou connecter une cellule prototype dans un storyboard '
clearlight
@binarystar Vous devez enregistrer une nib ou une classe de votre cellule personnalisée dans la vue did load. comme:[self.tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
GoodSp33d
6

Je n'ai jamais compris pourquoi Apple a créé la nouvelle méthode, dequeueReusableCellWithIdentifier: forIndexPath :. Leur documentation n'est pas complète et est quelque peu trompeuse. La seule différence que j'ai pu discerner entre les deux méthodes, c'est que cette méthode plus ancienne peut renvoyer nil, si elle ne trouve pas de cellule avec l'identifiant passé, tandis que la nouvelle méthode plante, si elle ne peut pas retourner une cellule. Les deux méthodes sont garanties de renvoyer une cellule, si vous avez correctement défini l'identifiant, et de créer la cellule dans un storyboard. Les deux méthodes sont également garanties de renvoyer une cellule si vous enregistrez une classe ou un xib et que vous créez votre cellule dans du code ou un fichier xib.

rdelmar
la source
3
La nouvelle méthode utilise le chemin d'index pour déterminer la taille appropriée de la cellule.
rob mayoff le
1
@robmayoff Mais cela a-t-il un sens? Sans la nouvelle méthode, la taille de la cellule peut toujours être définie correctement. La nouvelle méthode peut-elle offrir une certaine commodité?
fujianjin6471
1
Lisez le dernier paragraphe de ma réponse pour plus de détails.
rob mayoff le
Cela signifie-t-il que si toutes mes cellules sont de la même taille dans le tableau, peu importe la méthode que j'appelle?
Happiehappie
2
Si je fournis tableView.estimateHeight, la taille de la cellule sera également déterminée correctement. Je ne profite toujours pas de la nouvelle méthode.
Ryan
1

Pour faire court:

dequeueReusableCell(withIdentifier, for)ne fonctionne qu'avec des cellules prototypes. Si vous essayez de l'utiliser lorsque la cellule prototype est absente, l'application planterait l'application.

Hollemans M. 2016, Liste de contrôle du chapitre 2, IOS Apprentice (5e édition). pp: 156.

SLN
la source
-2

Je recommanderais d'utiliser les deux si vous utilisez du contenu généré dynamique. Sinon, votre application pourrait se bloquer de manière inattendue. Vous pouvez implémenter votre propre fonction pour récupérer une cellule réutilisable facultative. Si c'est le cas, nilvous devez renvoyer une cellule vide qui n'est pas 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
    }
}

Et l'extension pour renvoyer une cellule vide:

// 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 exemple complet de son utilisation:

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
la source