Chargement d'un UITableViewCell réutilisable à partir d'une pointe

89

Je suis capable de concevoir des UITableViewCells personnalisées et de les charger très bien en utilisant la technique décrite dans le fil de discussion trouvé à http://forums.macrumors.com/showthread.php?t=545061 . Cependant, l'utilisation de cette méthode ne vous permet plus d'initier la cellule avec un reuseIdentifier, ce qui signifie que vous devez créer de nouvelles instances entières de chaque cellule à chaque appel. Quelqu'un a-t-il trouvé un bon moyen de toujours mettre en cache des types de cellules particuliers pour les réutiliser, tout en étant capable de les concevoir dans Interface Builder?

Greg Martin
la source

Réponses:

74

Implémentez simplement une méthode avec la signature de méthode appropriée:

- (NSString *) reuseIdentifier {
  return @"myIdentifier";
}
Louis Gerbarg
la source
Où mettre en œuvre cette méthode?
Krishnan
2
Dans votre sous-classe UITableViewCell. developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/…
DenTheMan
5
C'est risqué. Que se passe-t-il si vous avez deux sous-classes de votre sous-classe de cellules et que vous utilisez les deux dans une seule vue de tableau? S'ils envoient l'appel d'identifiant de réutilisation à super, vous retirerez une cellule du mauvais type .............. Je pense que vous devez remplacer la méthode reuseIdentifier, mais lui faire renvoyer un identifiant supplanté chaîne.
SK9
3
Pour être sûr qu'il est unique, vous pouvez faire:return NSStringFromClass([self class]);
ivanzoid
119

En fait, puisque vous créez la cellule dans Interface Builder, définissez simplement l'identifiant de réutilisation ici:

IB_reuse_identifier

Ou si vous exécutez Xcode 4, vérifiez l'onglet Inspecteur d'attributs:

entrez la description de l'image ici

(Modifier: une fois que votre XIB est généré par XCode, il contient un UIView vide, mais nous avons besoin d'un UITableViewCell; vous devez donc supprimer manuellement l'UIView et insérer une cellule de vue de tableau. Bien sûr, IB n'affichera aucun paramètre UITableViewCell pour un UIView.)

Tim Keating
la source
Que dois-je définir les identifiants, si j'utilise ces cellules créées par Nib pour plus d'une cellule? Cela nous conduira à avoir deux cellules avec les mêmes identifiants.
Krishnan
4
Ne le considérez pas comme un identifiant unique - pensez-y plutôt comme un nom de type.
Tim Keating
Je ne vois pas l'option de définir un identifiant via le générateur d'interface intégré dans Xcode 4.3.3. Je suis définitivement en train de définir la classe sur ma sous-classe UITableViewCell. Suis-je juste en train de le manquer, ou est-ce parti
Tyler
3
Ok, j'ai résolu mon problème. Si vous commencez avec un objet UIView dans le générateur d'interface (dans Xcode 4) et que vous modifiez sa classe en UITableViewCell, vous n'obtenez pas les propriétés spécifiques à la cellule comme l'identificateur de réutilisation. Pour obtenir cela, vous êtes censé commencer avec un xib vide et faire glisser dans un objet de cellule de tableau, qui aura alors les propriétés spécifiques à la cellule que vous pouvez modifier.
Tyler
1
@Krishnan Pensez-y de cette façon - lorsque vous créez la cellule de vue tableau avec l'identifiant X, vous dites "Donnez-moi une cellule du pool étiquetée X". Si la piscine existe et qu'il y a une cellule libre là-dedans, elle vous la donne. Sinon, il crée le pool (si nécessaire), puis actualise la cellule, l'appelle «X», puis vous la remet. Ainsi, les cellules PEUVENT être uniques - par exemple, vous pouvez créer un pool avec une seule cellule avec un identifiant particulier - mais la bibliothèque utilise une stratégie de type liste libre pour éviter l'allocation / désallocation de mémoire.
Tim Keating
66

Maintenant, dans iOS 5, il existe une méthode UITableView appropriée pour cela:

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
wzs
la source
10
Les autres réponses de ce fil, y compris la réponse acceptée, contiennent des conseils obsolètes.
Kaelin Colclasure
est-ce rétrocompatible? Je veux dire que si je développe une application avec SDK 5.0 et cible minimum 4.0, l'application fonctionnera-t-elle sur des appareils avec iOS 4.0 par exemple?
Abolfoooud
1
Non, ce n'est pas rétrocompatible, comme toute nouvelle API dans iOS 5.0.
marzapower
exemple travaillé de la façon d'intégrer ceci dans votre contrôleur: mindfiresolutions.com
...
47

Je ne me souviens pas où j'ai trouvé ce code à l'origine, mais cela a très bien fonctionné pour moi jusqu'à présent.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"CustomTableCell";
    static NSString *CellNib = @"CustomTableCellView";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    // perform additional custom work...

    return cell;
}

Exemple de configuration d'Interface Builder ...

texte alternatif

arc en ciel
la source
12

Regardez la réponse que j'ai donnée à cette question:

Est-il possible de concevoir des sous-classes NSCell dans Interface Builder?

Il n'est pas seulement possible de concevoir un UITableViewCell dans IB, c'est souhaitable car sinon, tout le câblage manuel et le placement de plusieurs éléments sont très fastidieux. La performance est bonne tant que vous prenez soin de rendre tous les éléments opaques lorsque cela est possible. Le reuseID est défini dans IB pour les propriétés de UITableViewCell, puis vous utilisez l'ID de réutilisation correspondant dans le code lors de la tentative de retrait de la file d'attente.

J'ai également entendu certains des présentateurs de la WWDC l'année dernière dire que vous ne devriez pas créer de cellules de vue de tableau dans IB, mais c'est un tas de superpositions.

Kendall Helmstetter Gelner
la source
2
Vous ne devez pas créer de cellules de vue tableau dans IB si vous avez besoin de transparence et que vous voulez de bonnes performances de défilement. Pour certaines interfaces utilisateur, vous avez besoin de transparence (pour rendre du texte sur des graphiques, par exemple). Parfois, le seul moyen d'obtenir un bon défilement sur du matériel plus ancien (pré-A4) est de rendre le code, pour éviter que le GPU n'ait à composer plusieurs couches transparentes.
Nick Forge
2
C'est vrai, mais même avec cela, vous feriez peut-être mieux de laisser les performances souffrir légèrement sur les anciens appareils pour faciliter la maintenance des cellules intégrées dans IB. Vous pouvez également utiliser cette technique comme cellule de modèle à partir de laquelle vous dessinez ensuite des éléments pour éviter la composition avec une méthode de dessin personnalisée.
Kendall Helmstetter Gelner
6

Voici une autre option:

NSString * cellId = @"reuseCell";  
//...
NSArray * nibObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:nil options:nil];

for (id obj in nibObjects)
{
    if ([obj isKindOfClass:[CustomTableCell class]])
    {
        cell = obj;
        [cell setValue:cellId forKey:@"reuseIdentifier"];
        break;
    }
}
JDL
la source
Notez que c'est la seule solution publiée à ce jour qui ne nécessite pas de sous-classement de votre personnalisation UITableViewCellafin de définir une valeur unique pour reuseIdentifer. Je pense que c'est ce que l'op original recherchait réellement.
charshep
J'espère qu'Apple ne refusera pas ma demande d'utiliser ceci ... J'utilise ceci pour obtenir des cellules "statiques" parce que dans ma vue de tableau, le processus de remplissage d'une cellule est assez lent. De cette façon, je n'ai qu'à l'exécuter une fois (en donnant un identifiant différent à chaque ligne). Je vous remercie!
Ricard Pérez del Campo
Très utile car il n'y a pas de méthode UITableViewCell pour définir cela manuellement!
Jesse
2

Je crée mes cellules de vue personnalisées de la même manière - sauf que je connecte la cellule via un IBOutlet.

L' [nib objectAt...]approche est susceptible de changer les positions des éléments dans le tableau.

L' UIViewControllerapproche est bonne - il suffit de l'essayer et cela fonctionne assez bien.

MAIS...

Dans tous les cas, le initWithStyleconstructeur n'est PAS appelé, donc aucune initialisation par défaut n'est effectuée.

J'ai lu divers endroits sur l'utilisation de initWithCoderou awakeFromNib, mais aucune preuve concluante que l'un ou l'autre est le bon moyen.

En dehors d'appeler explicitement une méthode d'initialisation dans la cellForRowAtIndexPathméthode, je n'ai pas encore trouvé de réponse à cela.

sww
la source
awakeFromNib est la bonne façon de réagir à un objet chargé à partir d'un NIB.
Jon Hess
2

Il y a quelque temps, j'ai trouvé un excellent article de blog sur ce sujet sur blog.atebits.com , et j'ai depuis commencé à utiliser la classe Loren Brichter ABTableViewCell pour faire toutes mes UITableViewCells.

Vous vous retrouvez avec un simple conteneur UIView pour mettre tous vos widgets, et le défilement est ultra rapide.

J'espère que c'est utile.

Eric
la source
2

Cette technique fonctionne également et ne nécessite pas un ivar funky dans votre contrôleur de vue pour la gestion de la mémoire. Ici, la cellule de vue de tableau personnalisée se trouve dans un xib nommé "CustomCell.xib".

 static NSData *sLoadedCustomCell = nil;

 cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
 if (cell == nil) 
 {
   if (sLoadedCustomCell == nil) 
   {        
      // Load the custom table cell xib
      // and extract a reference to the cell object returned
      // and cache it in a static to avoid reloading the nib again.

      for (id loadedObject in [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil]) 
      {
        if ([loadedObject isKindOfClass:[UITableViewCell class]]) 
        {
          sLoadedCustomCell = [[NSKeyedArchiver archivedDataWithRootObject: loadedObject] retain];
          break;
        }
    }
    cell = (UITableViewCell *)[NSKeyedUnarchiver unarchiveObjectWithData: sLoadedCustomCell];
  }
Bill Garrison
la source
1
L'archivage et la désarchivage sont totalement inutiles.
Bryan Henry
1
L'archivage / désarchivage n'est pas nécessaire si vous êtes d'accord pour charger la cellule de sa pointe lorsqu'elle ne peut pas être retirée de la file d'attente. Cependant, si vous ne souhaitez charger la cellule à partir de sa pointe qu'une seule fois , vous devez la mettre en cache en mémoire. J'accomplis cette mise en cache en utilisant NSKeyedArchiving car UITableViewCell n'implémente pas NSCopying.
Bill Garrison
1
Cela dit, utiliser UINib pour charger la cellule produit le même effet: charger à partir du disque une fois, charger à partir de la mémoire par la suite.
Bill Garrison
2

La méthode Louis a fonctionné pour moi. C'est le code que j'utilise pour créer l'UITableViewCell à partir de la pointe:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"CustomCellId"];

    if (cell == nil) 
    {
        UIViewController *c = [[UIViewController alloc] initWithNibName:@"CustomCell" bundle:nil];
        cell = (PostCell *)c.view;
        [c release];
    }

    return cell;
}
ggarber
la source
2
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

return cell;
}
Mohit
la source
1

La solution gustavogb ne fonctionne pas pour moi, ce que j'ai essayé est:

ChainesController *c = [[ChainesController alloc] initWithNibName:@"ChainesController" bundle:nil];
[[NSBundle mainBundle] loadNibNamed:@"ChaineArticleCell" owner:c options:nil];
cell = [c.blogTableViewCell retain];
[c release];

Cela semble fonctionner. Le blogTableViewCell est l'IBOutlet de la cellule et ChainesController est le propriétaire du fichier.

groupe
la source
1

De la documentation UITableView concernant dequeueWithReuseIdentifier: "Une chaîne identifiant l'objet de cellule à réutiliser. Par défaut, l'identificateur d'une cellule réutilisable est son nom de classe, mais vous pouvez le changer en n'importe quelle valeur arbitraire."

Remplacer -reuseIdentifer vous-même est risqué. Que se passe-t-il si vous avez deux sous-classes de votre sous-classe de cellules et que vous utilisez les deux dans une seule vue de tableau? S'ils envoient l'appel d'identifiant de réutilisation à super, vous retirerez de la file d'attente une cellule du mauvais type .............. Je pense que vous devez remplacer la méthode reuseIdentifier, mais lui faire renvoyer un identifiant supplanté chaîne. Ou, si aucun n'a été spécifié, demandez-lui de renvoyer la classe sous forme de chaîne.

SK9
la source
0

Pour ce que ça vaut, j'ai demandé à un ingénieur iPhone à ce sujet lors de l'un des iPhone Tech Talks. Sa réponse a été: "Oui, il est possible d'utiliser IB pour créer des cellules. Mais ne le faites pas. S'il vous plaît, ne le faites pas."

août
la source
1
C'est étrange. Au moins deux des conférences lors de la conférence de New York comportaient un code de démonstration utilisant des cellules créées dans IB.
Shawn Craver
3
Je ne suis pas sûr de le croire car ils utilisent IB pour créer des cellules dans le projet d'exemple Advanced Table View Cells d'Apple.
iwasrobbed
Merci pour ça. Chaque fois que je l'ai fait, j'ai rencontré des problèmes. Cela pourrait être pourquoi
skorulis
Il n'en avait probablement pas assez de connaissances.
aryaxt
0

J'ai suivi les instructions d'Apple liées par Ben Mosher (merci!) Mais j'ai constaté qu'Apple avait omis un point important. L'objet qu'ils conçoivent dans IB n'est qu'un UITableViewCell, tout comme la variable qu'ils chargent à partir de celui-ci. Mais si vous le configurez en tant que sous-classe personnalisée de UITableViewCell et que vous écrivez les fichiers de code pour la sous-classe, vous pouvez écrire des déclarations IBOutlet et des méthodes IBAction dans le code et les connecter à vos éléments personnalisés dans IB. Ensuite, il n'est pas nécessaire d'utiliser des balises de vue pour accéder à ces éléments et vous pouvez créer n'importe quel type de cellule folle que vous voulez. C'est le paradis de Cocoa Touch.

David Casseres
la source