Créer une UITableViewCell personnalisée à partir d’Interface Builder

Le problème

La TableView est un composant essentiel de l’HIM de l’iPhone très simple à développer pour des besoins simples. Clairement, si on doit afficher à l’utilisateur des cellules avec une seule ligne de texte, ça se fait les doigts dans le nez.

Là où les choses se corsent, c’est quand il faut créer des cellules personnalisées. Avec, par exemple, deux UILabel et une UIImageView. En effet, il n’existe pas de méthode pour charger un nib (comme l’habituel initWithNibName:). Ainsi, on se retrouve forcé à créer tout nos éléments et à les placer à la main dans le code. C’est loin d’être quelque chose d’agréable, simple et rapide à faire. Sans compter qu’au moindre changement de design de la cellule, on perd un temps fou.

La solution

Non, l’idéal c’est de pouvoir utiliser Interface Builder pour construire ses cellules… Après quelques essais, recherches et erreurs, voici la solution que j’ai trouvé (et que j’utilise):

On commence par créer une nouvelle interface dans IB. On y ajoute une TableCell (au passage, le nib ne doit contenir que des TableCell’s !). Dans la TableCell, on ajoute les éléments dont on a besoin (ici, deux UILabel et un UIImageView). On définit la Class Identity et les Class Outlets qui vont bien. On finit par un Write Classfile et on a notre nib et l’UITableViewCell dans le projet.
tableviewcellxibscreen

Maintenant, il faut s’occuper de l’UITableViewController. L’utilisation d’un nib ne change absolument rien au fonctionnement habituel. Ce n’est qu’à la création de la cellule (dans - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath) que cela change un peu. Voilà ce que ça donne:

(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"MyCellIdent";
    MyTableViewCell *cell = (MyTableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
    	// On initialise un ViewController à partir du Nib qui a été fait
    	UIViewController *c = [[UIViewController alloc] initWithNibName:@"MyTableViewCell"  bundle:nil];
	// On récupère sa vue et on la caste dans le type de notre TableViewCell
	cell = (MyTableViewCell *)c.view;
	[c release];
    }
    // Ensuite, ça reste traditionnel
    cell.label1.text = [[myObjects objectAtIndex:indexPath.row] name];
    cell.label2.text = [[myObjects objectAtIndex:indexPath.row] desc];
    [cell.uIImageView  setImage:[[myObjects objectAtIndex:indexPath.row] image]];
    return cell;
}

Les développeurs avisés auront très certainement remarqué un petit détail qui a son importance: on ne définit jamais (lors de la création de la cellule) son CellIdentifier, ainsi la réutilisation de cellules devient impossible (ce qui est suicidaire en terme de mémoire). En fait, celui-ci se définit dans Interface Builder. Dans les propriétés de votre TableViewCell, il suffit de mettre votre CellIndetifier dans Identifier. Ici, on mettra donc MyCellIdent.

Vous voilà maintenant avec des TableViewCell générée à partir d’Interface Builder en gardant possible le mécanisme de ré-utilisation de cellules. Ça, c’est des demies journées de gagnées sur vos projets (si ils comportent des cellules personnalisées bien évidement).

Édition du 09/01/09:
Un exemple de code complet et opérationnel est disponible dans le billet Créer une UITableViewCell personnalisée à partir d’Interface Builder: exemple complet

8 réactions à l'heure actuelle »

  1. Bruno da Silva a écrit

    le 8 janvier 2009 à 10:34

    Bonjour,

    J’ai trouvé très intéressant cette manière de faire, mais je comprends pas vraiment ce qu’il faut créer ensuite comme classes. Serait-ce possible d’avoir le code exemple complet?

    Je vous remercie d’avance

    Bruno

  2. Julien Quéré a écrit

    le 8 janvier 2009 à 19:58

    Bonsoir Bruno,

    Je n’ai pas de code complet qui puisse être public sous la main …

    Le vais voir ce que je peux faire demain ou lundi. Je pense pouvoir sortir un truc concret d’ici là.

  3. Julien Quéré a écrit

    le 9 janvier 2009 à 23:56

    Bonsoir Bruno,

    Un exemple de code complet est disponible dans ce billet:
    http://webd.fr/224-creer-uitableviewcell-personnalisee-interface-builder-exemple

    Bonne soirée !

  4. Jonathan Teboul a écrit

    le 3 avril 2009 à 14:37

    Bonjour Julien,

    j’ai commencé à développer sur Iphone, il y a de cela une semaine. Avant hier soir, j’ai essayé d’utiliser une UiTableViewCell personnalisée, mais il y avait toujours quelque chose qui n’alllait pas. Sur le site d’apple, les sources traitent peu des objetss créés avec IB, mais directmement dans le code. J’ai cherché hier soir sur le net et je suis tombé sur ta page assez rapidement je dois dire. La seule lecture de ce billet (sans le code complet) m’a permis de réaliser ce que je souhaitais. Tout ca pour te remercier!

    Jon

  5. Julien Quéré a écrit

    le 5 avril 2009 à 8:23

    Mais de rien Jonathan :) Ravi de voir que ça a pu t’aider !

  6. Webd » Une UITableView triée par ordre alphabétique a écrit

    le 20 avril 2009 à 17:59

    [...] Builder (pour plus de détails sur la manipulation de UITableViewCell avec Interface Builder, voir Créer une UITableViewCell personnalisée à partir d’Interface Builder). Maintenant, faisons une structure pour nos données. Ce sera une simple classe contenant 4 [...]

  7. Dimitri a écrit

    le 28 avril 2009 à 9:10

    Super ca marche :) par contre juste une petite remarque au niveau des lignes :
    cell = (MyTableViewCell *)c.view;
    [c release];
    Le petit truc que je remarque c’est que « cell » pointe sur « c.view » (aucun retain donc), puis un [c release] est fait, et donc tout ce qui se trouve dans le controller « c » est released, y compris la « view » (c.view). Comme cell pointe sur c.view, cell sera egalement released…non ? peut etre me trompe-je ? :)
    enfin voila
    a+

  8. Julien Quéré a écrit

    le 2 mai 2009 à 16:33

    Bonjour Dimitri,

    Tout d’abord, désolé pour le retard dans ma réponse … En fait, il faut considérer les objets en Objective-C comme des chiens.

    Un chien, tant qu’il a une laisse au cou: il reste en place. Dès que plus rien ne le retien: il s’enfuit.

    Là, j’ai mon objet c. Je lui fais un alloc. J’en suis donc responsable. Si c’était un chien, je lui aurait mis une laisse.

    Ensuite, je passe sa référence à cell. Par conséquent, j’incrémente le compteur de références de c. Je rajoute une laisse au chien.

    Quand je fais le release sur c, je décrémente le compteur de 1 (j’enlève une laisse au chien). Mais il me reste toujours 1 dans le compteur de références.

    Par conséquent, ce n’est qu’une fois que cell sera release que c sera libéré.

    J’espère ne pas m’être trop mélangé dans mon explication.

    Bonne journée !

Fil RSS des commentaires · URI de trackbacks (modérés ...)

Laisser un commentaire

Un nom (requis)

Un mail (requis)

Peut être une URL ?

Réaction: