Comment rendre un UITableViewCell avec des sous-vues différentes réutilisable?

J'ai un UITableView dans lequel UITableView , naturellement, UITableViewCells qui sont tous de la même class, appelons cela MyCell . J'ai donc un MyCell.xib , un MyCell.h et un MyCell.m .

Malheureusement, ces cellules contiennent une sous-vue, qui contient un contenu variable, par exemple une sous-vue de train subview et une sous-vue de car subview . Donc, si le UITableView besoin d'une nouvelle cellule, c'est toujours un MyCell mais parfois il contient une sous-vue de train et parfois une sous-vue de voiture.

Maintenant, voici mon problème: Comment rendre MyCell correctement réutilisable? La cellule elle-même est réutilisable comme prévu (Dans le .xib j'ai défini son identifiant) mais sa sous-vue doit être créée encore et encore pour chaque cellule. Ma première idée était de changer l'identifiant de MyCell fonction de son contenu, mais malheureusement, reuseIdentifier ne peut pas être modifié à l'exécution. Je pourrais, cependant, implémenter mon propre - (NSSsortingng *) reuseIdentifier {} que je suppose que cela fonctionnerait, bien que je ne considérerais pas que c'est un grand style. Y a-t-il une meilleure manière de faire cela?

Merci d'avance!

EDIT: Je me rends count que j'ai besoin d'append que les sous-vues sont stockées dans leurs propres classs / xibs pour garder leur code séparé.

Au lieu d'append des sous-vues aux cellules, je vous suggère de créer pour chaque type de cellule votre propre class. Si vous avez les types: train, voiture, vélo, bateau et avion, je créerais cinq sous-classs.

Comme je comprends Apple le mécanisme de réutilisation avec l'identifiant est juste pour ce cas: différents types de cellules obtiennent leur propre identificateur, pas chaque cellule une spéciale. Juste pour montrer comment j'interprète le tout.

Dans le Guide de programmation Table View d'Apple pour iOS / Caractéristiques des objects cellulaires , le 3ème paragraphe donne un aperçu de la signification de l'identificateur de réutilisation.

J'ai moi-même écrit une petite class TableViewCellFactory qui me rend la vie plus facile de créer des cellules avec le constructor de l'interface et les avoir dans mon application en quelques minutes.

Tout d'abord un petit exemple sur comment utiliser cellForRowAtIndexPath et l'usine ainsi que le réglage du contenu pour une cellule.

Je crée une nouvelle cellule avec l'usine qui a besoin de tableView pour pouvoir gérer la logique de réutilisation. La prochaine chose est de laisser une méthode remplir le contenu de la cellule. Dans ce cas, c'est une cellule qui montre un clip video avec du text.

Méthode de délégué de source de données et assistant

 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath { VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView]; [self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath]; // code to decide what kind of cell not shown, but it could be here, just move the model // access code from the configure cell up here and decide on what you get return cell; } 

Ensuite vient l'assistant de source de données pour mettre du contenu dans la cellule. Obtenez le contenu de ma masortingce de templates et définissez les propriétés. Notez, cela fait tout par reference, rien n'est returnné.

 - (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath { VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row]; aCell.videoTitleLabel.text = videoClip.title; aCell.dateLabel.text = videoClip.date; // more data setting ... } 

TableViewFactory

Cette class se compose principalement de methods de commodité et de quelques methods standard pour faire le vrai travail.

 // Convenience static method to create a VideoClipTableViewCell + (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView { return [self loadCellWithName:@"VideoClipTableViewCell" tableView:aTableView]; } // method to simplify cell loading + (id)loadCellWithName:(NSSsortingng *)aName tableView:(UITableView *)aTableView { return [self loadCellWithName:aName className:aName identifier:aName tableView:aTableView]; } // method with actually sortinges to create the cell + (id)loadCellWithName:(NSSsortingng *)aName className:(NSSsortingng *)aClassName identifier:(NSSsortingng *)anIdentifier tableView:(UITableView *)aTableView { UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier]; if (cell == nil) { UINib * nib = [UINib nibWithNibName:aName bundle:nil]; NSArray * nibContent = nil; nibContent = [nib instantiateWithOwner:nil options:nil]; for (id item in nibContent) { if ([item isKindOfClass:NSClassFromSsortingng(aClassName)]) { cell = item; } } } return cell; } 

J'ai jeté toute l'erreur et la gestion des exceptions juste pour garder l'exemple court. Si quelqu'un est intéressé, j'appendais le code.

Certaines choses importantes à propos de l'utilisation est:

  • Le nom de la class connectée, l'identificateur de réutilisation et le nom de la plume sont tous les mêmes, de sorte qu'une cellule peut être créée avec une seule string de caractères, sinon le loadCellWithName long doit être utilisé.

  • N'oubliez pas de définir l'identifiant de réutilisation dans le constructor d'interface.

  • La plume devrait contenir seulement un TableViewCell (peut être changé avec un peu de encoding cependant)

  • Ne pas définir les points de vente du propriétaire du file, utilisez ceux de la tableViewCell

  • Définir l'identité de class de la cellule à une class correspondante qui doit être créée avant tout

  • Regardez la capture d'écran

entrez la description de l'image ici

Réflexions sur le sous-classment de ses propres cellules personnalisées

Il est en fait facile de sous-classr votre propre cellule, d'y append quelques propriétés, de les rendre disponibles dans IB avec des sockets, de choisir la nouvelle class étendue dans IB pour votre file nib.

Le problème principal est l'interface elle-même. Il n'est pas facile d'avoir différents types de cellules basés sur une cellule personnalisée dans le constructor d'interface. La première approche consisterait à copyr le file nib, à le renommer et à l'utiliser avec toutes les references existantes et à lier les nouvelles à différents points de vente. Mais que se passe-t-il si la cellule de base doit être changée? Passer par toutes sortes de cellules héritantes pourrait être une tâche fastidieuse.

J'ai juste trébuché sur les vues personnalisées dans Interface Builder en utilisant IBPlugins sur Cocoa with Love. C'est un joli tutoriel comment étendre la bibliothèque de composants dans IB. Notre cellule de base personnalisée pourrait devenir un élément de la bibliothèque et devenir le model que nous recherchions. Je pense que cette approche est la bonne façon de choisir. Toujours, en regardant les étapes nécessaires, ce n'est pas seulement fait dans les 5 minutes.

Interface Builder est un outil utile, nous permettant de créer rapidement des vues, mais quand il s'agit de la réutilisation à travers la sous-class, il y a de grandes étapes nécessaires pour créer des vues maintenables. Dommage.

Créer des vues avec du code uniquement Je pense qu'il vaut mieux sous-classr s'il y a plus d'un niveau d'inheritance ou plusieurs classs d'ancêtres pour une seule vue de base.

MODIFIER

D'un autre côté, Apple met en garde contre l'utilisation excessive de sous-vues dans une cellule:

Toutefois, si le contenu d'une cellule est composé de plus de trois ou quatre sous-vues, les performances de défilement peuvent en souffrir. Dans ce cas (et surtout si la cellule n'est pas modifiable), envisagez de dessiner directement dans une sous-vue de la vue de contenu de la cellule. L'essentiel de cette directive est que, lors de la mise en œuvre de cellules de vue table personnalisées, sachez qu'il existe un compromis entre les performances de défilement optimales et les performances optimales d'édition ou de réorganisation.

À l'heure actuelle, toute approche a ses inconvénients et avantages:

  • Trop de sous-vues de l'homme vont bash la performance, facilement fait avec IB

  • Dessiner avec du code se traduira par une base de code difficile à maintenir mais fonctionnera mieux

  • Le saut IB rend la sous-classification des classs de cellules de model plus facile

  • Hiérarchie par sous-class difficile à atteindre avec IB avec files nib

Il y a plusieurs façons de le faire. Vous avez besoin d'un moyen d'accéder à cette sous-vue et de la réinitialiser ou de la modifier lors de la réutilisation.

  1. Vous pourriez sous-classr UITableViewCell avec votre propre class de cellule qui a une propriété pour la vue de train ou de voiture. De cette façon, vous pouvez accéder et modifier cette vue lorsque la cellule est réutilisée.

  2. Affectez un identifiant différent à chaque type de cellule:

`

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSSsortingng *CarCellIdentifier = @"CarCell"; static NSSsortingng *TrainCellIdentifier = @"TrainCell"; if(indexPath == carCellNeeded) { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease]; [cell addSubview:carView]; } } else if(indexPath == trainCellNeeded){ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease]; [cell addSubview:trainView]; } } return cell; } 
  1. Vous pouvez également atsortingbuer une balise spéciale à la sous-vue que vous ajoutez et lorsque la cellule revient à nouveau pour être réutilisée, vous pouvez accéder à cette sous-vue spécifique à l'aide de sa balise.

J'appendais les deux sous-vues personnalisées à la plume et les relierais à une prise. Et selon le contenu je cacherais l'un d'eux quand vous configurerez le contenu de votre cellule.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSSsortingng *CellIdentifier = @"CellIdentifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = /* load from nib */ } if (/*indexPath conditionForTrainCell*/) { cell.trainSubview.hidden = NO; cell.carSubview.hidden = YES; // configure train cell } else { cell.trainSubview.hidden = YES; cell.carSubview.hidden = NO; // configure car cell } return cell; } 

le plus simple est de créer une sous-class UITableViewCell personnalisée et de créer un xib pour celle-ci. Définissez la vue racine dans le xib comme uitableviewCell et définissez sa class sur votre sous-class UITableViewCell. Définissez la class du propriétaire du file en tant que sous-class TableViewController et ajoutez toutes les sous-vues souhaitées. Ensuite, vous pouvez simplement:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSSsortingng * CellIdentifier = @"cellIdentifier"; TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[NSBundle mainBundle] loadNibNamed:NSSsortingngFromClass([TableViewMessageCell class]) owner:self options:nil] lastObject]; } Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.message = message; return cell; }