2 votes

TableView CellReuse

J'ai des problèmes avec mon fichier de cellules personnalisées dans Tableview. J'ai réussi à le faire en utilisant la ligne de commentaire ci-dessous, mais les performances étaient vraiment mauvaises lorsqu'il y avait plus de 10 cellules. L'utilisation de DequeueReusableCell conduit à cette erreur :

NSInternalInconsistencyException', reason : 'unable to dequeue a cell with identifier DiveNewsShort - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'.

ce qui est étrange, car j'enregistre bien la nib dans viewDidLoad(). J'espère que vous pourrez m'aider, je suis de plus en plus frustré par cette situation.

class ProfilTableView: UITableViewController {

  override func viewDidLoad() {

    super.viewDidLoad()

    tableView.register(UINib(nibName: "DiveNewsShort", bundle: nil), forCellReuseIdentifier: "DiveNewsShort")

    tableView.register(DiveNewsShort.self, forCellReuseIdentifier: "DiveNewsShort")
  }

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

  // let cell = Bundle.main.loadNibNamed("DiveNewsShort", owner: self, options: nil)?.first as! DiveNewsShort
  // This one works as expected

    let cell = tableView.dequeueReusableCell(withIdentifier: "DiveNewsShort", for: indexPath) as! DiveNewsShort
  // This one does not

return cell }

StoryBoard Picture


Mise à jour :

J'ai réussi à me débarrasser de l'erreur en ajoutant la fonction registre dans la fonction cellForRowAt, mais je ne pense pas que ce soit une méthode efficace. Cela devrait fonctionner dans la fonction vieDidLoad, n'est-ce pas ?

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

    tableView.register(UINib(nibName: "DiveNewsShort", bundle: nil), forCellReuseIdentifier: "DiveNewsShort")
    let cell = tableView.dequeueReusableCell(withIdentifier: "DiveNewsShort", for: indexPath) as! DiveNewsShort

return cell }

3voto

Max Pevsner Points 2961

Vous n'avez pas besoin de cette ligne :

tableView.register(DiveNewsShort.self, forCellReuseIdentifier: "DiveNewsShort")

Vous avez déjà enregistré le fichier nib une ligne auparavant.

1voto

Rob Points 70987

Il existe trois façons d'enregistrer les cellules pour la réutilisation/la mise en réserve :

  1. Vous créez les cellules de manière programmatique, auquel cas vous enregistrez la classe dans le fichier viewDidLoad .

  2. Vous utilisez un NIB, dans ce cas vous enregistrez le NIB dans viewDidLoad .

  3. Vous utilisez des prototypes de cellules de storyboard, auquel cas vous n'avez pas besoin d'enregistrer quoi que ce soit. Le storyboard fait tout cela pour vous.

Puisque vous utilisez des NIBs, vous devriez supprimer l'enregistrement de la classe et enregistrer uniquement le NIB. Et vous devriez faire cela dans viewDidLoad . Ce processus est décrit dans https://stackoverflow.com/a/28490468/1271826 ainsi que dans Réponse de Reinier .

Regarder votre MCVE votre problème était le résultat d'une erreur plus fondamentale, où vous avez eu une UIViewController en essayant d'utiliser un autre contrôleur de vue, qui était un UITableViewController pour gérer la table. Mais UITableViewController a sa propre UITableView et n'utilisera pas celui que vous avez une @IBOutlet pour, donc vous enregistriez le NIB pour une vue de table que vous ne voyiez pas. Il y avait une tonne d'autres problèmes ici (par exemple, si vous voulez vraiment un contrôleur de vue à l'intérieur d'un contrôleur de vue, vous devez faire des appels de confinement du contrôleur de vue, etc. UITableViewController du projet et lorsque cela a été corrigé, il fonctionne exactement comme nous l'avons décrit. Voir https://github.com/robertmryan/Divers pour une version fonctionnelle de votre MCVE.

Vous n'avez pas non plus branché les prises pour les deux autres commandes de votre cellule (l'interrupteur et le curseur). Ainsi, si vous modifiez l'un de ces deux contrôles et que vous faites ensuite défiler la page, les cellules sont réutilisées et vous voyez le contrôle UIKit modifié qui a été fait pour une autre cellule, mais qui a été réutilisé par la suite. Pour remédier à cela, il faut que votre UITableViewCell La sous-classe devrait avoir des points de vente pour tous les contrôles, et cellForRowAt doit définir des valeurs pour tous ces points de vente. Vous avez également besoin d'un mécanisme permettant à la cellule d'informer le contrôleur de vue lorsque l'interrupteur et le curseur ont été modifiés et de mettre à jour le modèle en conséquence. cellForRowAt a été appelé ultérieurement pour cette ligne, il connaîtrait l'état de cette CellData pour régler la commande de manière appropriée. Une solution courante pour cela consiste à utiliser le modèle protocole-délégué. Voir le repo GitHub ci-dessus, qui illustre également ce modèle.

0voto

Reinier Melian Points 13437

J'ai construit ce protocole pour m'aider dans ce processus.

protocol CBNibInstanceableCellProtocol {
    static func getCellXib() -> UINib?

    static func getReuseIdentifier() ->String
}

et dans votre classe vous devez implémenter ces méthodes comme ici

//example implementation
extension CBUsersAttendanceEmptyCell : CBNibInstanceableCellProtocol
{
    static func getCellXib() -> UINib?
    {
        if Bundle.main.path(forResource: "CBUsersAttendanceEmptyCell", ofType: "nib") != nil
        {
            return UINib(nibName: "CBUsersAttendanceEmptyCell", bundle: nil)
        }
        return nil
    }

    static func getReuseIdentifier() ->String
    {
        return "CBUsersAttendanceEmptyCell"
    }
}

alors dans votre viewDidLoad vous devez faire quelque chose comme ceci

//example code
self.collectionView.register(CBUsersAttendanceAvatarCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceAvatarCell.getReuseIdentifier())
self.collectionView.register(CBUsersAttendanceCountCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceCountCell.getReuseIdentifier())
self.collectionView.register(CBUsersAttendanceEmptyCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceEmptyCell.getReuseIdentifier())

dans votre cellForRow

if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CBUsersAttendanceCountCell.getReuseIdentifier(), for: indexPath) as? CBUsersAttendanceCountCell
        {
            return cell
        }

Vous devez avoir défini la classe de votre vue dans votre xib, c'est très important, vérifiez ces images.

enter image description here

enter image description here

J'espère que cela vous aidera

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X