Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Afficher une UITableView triée alphabétiquement avec un titre pour chaque section

Compatible iOS 3. Cliquer pour en savoir plus sur les compatibilités.Compatible iOS 4. Cliquer pour en savoir plus sur les compatibilités.Par simplearetenir (Simplearetenir), le 12 juillet 2010

Voici un tutoriel qui vous expliquera comment implémenter une tableView triée alphabétiquement avec un titre pour chaque section, comme ceci:

Prérequis Savoir implémenter une UITableView et ses protocoles UITableViewDataSource et UITableViewDelegate. Il existe déjà d'autres tutoriels sur ce site qui vous explique comment implémenter une tableView, je ne vais donc pas y revenir dans celui-ci, je me contenterai de vous expliquer les différences pour arriver à une tableView triée. On partira donc d’un contrôleur implémentant déjà UITableViewDataSource et UITableViewDelegate.

On part aussi du principe que la tableView affiche des éléments qui ont une structure du type :

@interface Element : NSObject {
    NSString *info1;
    NSString *info2;
    NSString *info3;
}

Je vous conseille d’utiliser des noms de propriétés un peu plus clair que mon exemple.

Trions, trions...

Nous allons commencer par quelques méthodes de manipulation de données. Vous pouvez soit les intégrer au contrôleur contenant la tableView, soit encore mieux créer une classe spécifique qui ne fera que ça, avec un nom du type ElementDataSource. Cette classe contiendra :

  1. Une liste (un NSArray que l’on peut appeler : elementArray) qui contient tous nos objets Element.
  2. Un dictionnaire (un NSMutableDictionary : elementDictionary)qui contiendra nos Element triés. Chaque entrée aura comme clef le tri et en valeur on aura une liste d’Element.
  3. Une liste (un NSArray que l’on peut appeler : elementTitleArray) contenant seulement les noms des sections, celle-ci sera bien évidement triée.

Je n’explique pas ici comment créer la classe ElementDataSource avec ses initialisations et sa méthode dealloc. Vous devrez aussi initialiser et créer les méthodes pour remplir elementArray. Je vous invite à lire d’autres tutoriels sur ce sujet.

On commence par la methode qui va nous permettre de créer le dictionnaire elementDictionary. On passe le paramètre key, qui va permetre de choisir la propriété de element sur lequel va se faire le tri.

	- (void) setupElementDictionaryWithKey:(NSString*)key {
Pour choisir la propriété sur laquelle va se faire le tri, il suffira de passer le nom de cette propriété sous forme d'une chaine. Pour notre exemple, on passera donc simplement la chaine : @“info1” ou @“info2” ou encore @“info3”.

on initialise le dictionnaire :

	[self setElementDictionary:[[NSMutableDictionary alloc] init]];

On boucle sur tous les element de elementList:

	for(Element *element in elementList) 
	{

Pour chaque Element, on récupère la chaine qui va nous servir de titre pour les sections de la tableView. Ici on veut la première lettre, pour cela on utilise la méthode substringToIndex. On utilise [element valueForKey:key] pour nous retourner la propriété sur laquelle porte le tri.

	NSString *title = [[element valueForKey:key] substringToIndex:1];
Ici pour récupérer la propriété de Element on utilise le principe du KVC pour Key Value Coding, c'est un modèle de conception implémenter dans cocoa qui permet de récupérer une propriété d'un objet grâce à son nom (clef) mais ça on en reparlera dans un tutoriel spécifique.

On cherche dans notre dictionnaire une entrée pour cette première lettre, et comme je vous l'ai dit, une entrée possède comme clef le titre de notre section et comme valeur une liste d'Element, donc on récupère une liste (array) :

	NSMutableArray *anArray = [elementDictionary valueForKey:title];

Si notre retour (anArray) vaut nil, c'est que le dictionnaire ne contient aucune entrée possédant la clef que l'on cherche. Il faut donc créer cette entrée, avec comme valeur une array (vide, on ne sait pas encore ce qu'il va y avoir dedans) :

	if (anArray == nil)
		[elementDictionary setObject:[NSMutableArray array] forKey:title];

Ensuite il suffit d'ajouter l'element courant dans l'entrée correspondante dans le dictionnaire (celle que l'on a trouvé ou celle que l'on vient de créer).

	[[elementDictionary valueForKey:title] addObject:element];
}

On ferme la boucle.

A ce moment le dictionnaire possède plusieurs entrées, chaque entrée a comme clef le titre d'une section et comme valeur une liste d'element. Maintenant il va falloir trier ces listes. Pour notre exemple, nous avons choisi un tri par ordre alphabetique sans casse. On commence par notre elementTitleArray, qui je vous le rappelle contiendra tous nos titres de sections. On l'initialise (en récupèrant chaque clefs de notre dictionnaire) et on le trie :

elementTitleArray  = [[elementDictionary allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

On boucle sur chaque clefs du dictionnaire :

for (NSString *title in elementTitleArray) {

On crée une description du type de tri que l'on veut faire, c'est un objet NSSortDescriptor. Pour cela on initialise un NSSortDescriptor en lui passant en paramètre le champ qu'il va utiliser pour faire le tri, le sens du tri (ascendant ou descendant) et le fait qu'il faut ignorer la casse.

	NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];

On peut créer plusieurs descriptors, il faut les ajouter dans un NSArray pour pouvoir les passer à la méthode sortUsingDescriptors. Ici il n'y en a qu'un :

	NSArray *descriptors = [NSArray arrayWithObject:descriptor];

On récupère la liste des element à trier et on lui demande de se trier en respectant nos descriptors :

	[[elementDictionary objectForKey:title] sortUsingDescriptors:descriptors];

On fait un peu de nettoyage, en relâchant notre descriptor :

	[descriptor release];
	}

On ferme cette dernière boucle et on en a terminé avec cette première méthode.

Il faudra lancer cette méthode avant la construction de la listView, dans le viewDidLoad du contrôleur par exemple.

Affichons, affichons...

On revient dans notre contrôleur (si on l'a quitté pour une classe ElementDataSource). Vous avez déjà par vous-même implémenté les protocoles UITableViewDataSource et UITableViewDelegate. Il y a cependant quelques lignes a ajouter dans l'implémentation traditionnelle :

Pour le nombre de sections dans la table, c'est tout simplement le nombre d'éléments contenus dans elementTitleArray :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
	return [elementTitleArray count];
}

Pour le nombre de lignes dans chaque section, ça se complique. On revient a notre dictionnaire (elementDictionary), chaque entrée représentant une section, avec comme clef sont titre et comme valeur la liste des element qui la compose, pour connaitre le nombre de lignes de chaque section il suffit donc de renvoyer ce nombre d'element.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ 
	NSString *title = [elementTitleArray objectAtIndex:section];
	return [[elementDictionary objectForKey:title] count];
}

Pour afficher le titre de chaque section, c'est très simple on implémente la méthode titleForHeaderInSection en utilisant notre elementTitleArray. Un élément de notre tableau représente une section de notre future listView alors il suffit de renvoyer chaque élément :

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
	return [elementTitleArray objectAtIndex:section];
}

On termine par un petit changement dans la méthode cellForRowAtIndexPath, je vais vous expliquer comment récupérer un element à partir du NSIndexPath. On récupère d'abord le titre de la section courante grâce au indexPath.section :

	NSString *title = [elementTitleArray objectAtIndex:indexPath.section];

Ensuite on récupère la liste des element de cette section dans notre elementDictionary :

	NSArray *arrayOfElement = [elementDictionary objectForKey:title];

Et pour finir on obtient l'element demandé avec l'indexPath.row:

	Element *element = [arrayOfElement objectAtIndex:indexPath.row];

Conclusion

Voilà vous êtes maintenant capable de trier votre liste et de l'afficher en section. Je suis resté assez vague sur les principes d'implémentation d'une listeView et cela pour ne pas empiéter sur d'autre tutoriel de ce site.