11

Listes d’éléments

L’une des habitudes ergonomiques les plus ancrées chez les utilisateurs d’iPhone/iPad est la navigation dans une liste avec le doigt. Ce type de navigation est omniprésent dans les applications et il est normal que le SDK fournisse un maximum d’éléments pour faciliter l’implémentation de listes d’éléments, appelées TableView (classe UITableView).

Les deux types de listes

Comme nous l’avons vu au chapitre 5, il existe deux sortes de listes d’éléments : les listes simples et les listes groupées. Techniquement, il y a peu de différences pour le développeur. Dans les deux cas, les données à représenter sont séparées en sections, puis en lignes.

Les listes simples

Les listes simples révèlent des données qui occupent toute la largeur de l’écran. Les sections (si elles sont utilisées) sont représentées par des barres horizontales contenant les titres et pieds de sections.

Les listes groupées

Les listes groupées exposent différents groupes de données qui peuvent être hétérogènes.

image

Figure 11–1 Une liste d’éléments en mode normal

Chaque section de la liste est représentée comme un bloc à bords arrondis.

image

Figure 11–2 Une liste d’éléments en mode groupé

Créer une TableView

La classe UITableView prend en charge l’affichage d’une liste d’éléments. On crée une instance à l’aide de l’initialisateur initWithFrame:style: qui prend en premier paramètre la frame dans laquelle dessiner la liste et en second paramètre le style de la vue UITableViewStylePlain ou UITableViewStyleGrouped.

Fournir des données à une TableView

Les design patterns utilisés pour les TableView sont similaires à ceux étudiés au chapitre précédent sur les PickerView.

Le schéma suivant résume le fonctionnement d’une TableView :

image

Figure 11–3 Après avoir alloué la tableView dans la vue, le delegate et le dataSource sont positionnés.

Le dataSource va répondre à la méthode numberOfComponents:inTableView : et retourner le nombre de sections à allouer.

Chacun de ces composants va invoquer à son tour la méthode tableView:numberOfRows :inSection : pour renvoyer le nombre d’éléments pour chaque section créée.

Enfin, pour chaque ligne, la méthode pickerView:cellForRowAtIndexPath : remplira les éléments avec des cellules constituées de texte et d’images.

Le delegate déclenchera la méthode tableView:didSelectRowAtIndexpath : lorsque l’utilisateur effectuera une sélection, permettant de traiter l’élément sélectionné.

Pour obtenir les données à afficher, la liste d’éléments utilise une source de données qui doit implémenter le protocole UITableViewDataSource. Le développeur doit également fournir un délégué implémentant le protocole UITableViewDelegate.

Décider du nombre de sections

Dans le cas où il y aurait plusieurs sections, il faut indiquer le nombre de celles-ci à l’aide de la méthode : numberOfSectionsInTableView: (par défaut, il n’y a qu’une seule section).

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

{

return 3;

}

Indiquer le nombre de lignes

La première méthode à implémenter est tableView:numberOfRowsInSection:. Elle prend en paramètres la table et le numéro de la section et elle renvoie le nombre d’éléments contenus dans cette section. Si vous n’en utilisez qu’une seule, il s’agit du nombre d’éléments dans la liste. Si on utilise une liste (par exemple une NSMutableArray), on peut appliquer la méthode count de ce type d’objets pour compter les éléments que la liste contient.

- (NSInteger)tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section

{

return 5;// Ou return [liste count] ;

}

Afficher des données

Lorsque la liste d’éléments nécessite d’afficher des données, elle appelle la méthode tableView:cellForRowAtIndexPath: de sa source de données. Cette méthode doit renvoyer un objet UITableViewCell, qui est une vue représentant une des cellules.

Pour réduire la consommation mémoire et améliorer la fluidité du défilement, un mécanisme de recyclage des cellules est proposé et doit impérativement être utilisé. En appelant la méthode dequeueReusableCellWithIdentifier: de l’objet UITableView, on récupère une cellule qui peut être réemployée ou nil si aucune n’est disponible.

Si on a pu récupérer une cellule, on se contente de redéfinir son contenu pour refléter la ligne à afficher. Sinon, on crée une nouvelle cellule, qui sera recyclée plus tard, et on détermine son contenu.

Les cellules doivent posséder un identifiant (Identifier) qu’on retrouve dans le Storyboard.

- (UITableViewCell *) tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

// L’identifiant doit aussi apparaître dans le Storyboard

static NSString *CellIdentifier = @"Cellule";

UITableViewCell *cell =

[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell =

[[UITableViewCell all oc] initWithStyle:UITableViewCellStyleDefault

reuseIdenti fier:CellIdentifier];

}

// Configuration de la cellule…

cell.textLabel.text = [NSString stringWithFormat:@"%@",[liste1

objectAtIndex:indexPath.row]];

[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];

return cell;

}

REMARQUE Les styles de cellule

Il existe plusieurs types de cellules prédéfinis que vous pouvez utiliser.

UITableViewCellStyleDefault affiche une cellule contenant un seul label et une image à gauche ; c’est le style par défaut.

UITableViewCellStyleValue1 affiche une cellule avec deux libellés, l’un à gauche et l’autre à droite. Le texte de gauche est noir et aligné à gauche, tandis que le texte de droite est bleu et aligné à droite. C’est le style utilisé dans l’application Réglages.

UITableViewCellStyleValue2 affiche une cellule avec deux libellés, un texte bleu dans la partie gauche de la cellule qui est aligné à droite et un texte noir aligné à gauche dans la partie droite de la cellule. C’est le style utilisé dans l’application Téléphone.

UITableViewCellSubtitle affiche une cellule avec deux libellés, un texte noir aligné à gauche sur la première ligne et un texte gris légèrement plus petit sur la deuxième ligne. C’est le style utilisé dans l’application iPod.

Vous pouvez adapter légèrement chacun de ces styles en modifiant la police, la couleur du texte, l’alignement, etc. grâce aux propriétés textLabel et detailTextLabel. Elles donnent un accès direct à l’objet UILabel utilisé pour afficher les deux textes.

Si les styles par défaut ne correspondent pas exactement à vos besoins, vous devez personnaliser la cellule. Nous verrons un peu plus loin dans ce chapitre quelles sont les différentes techniques possibles pour contrôler de manière beaucoup plus fine le contenu de la cellule et afficher des contenus riches (plusieurs lignes de texte, images, etc.).

Définir les en-têtes et pieds de sections

Afin de fournir les textes à afficher dans les en-têtes et pieds de sections, la source de données peut implémenter les méthodes optionnelles suivantes :

tableView:titleForHeaderInSection: et

tableView:titleForFooterInSection:

- (NSString *) tableView:(UITableView *)tableView

titleForHeaderInSection:(NSInteger)section

{

return [NSString stringWithFormat:@"Header %i", section];

}

- (NSString *) tableView:(UITableView *)tableView

titleForFooterInSection:(NSInteger)section

{

return [NSString stringWithFormat:@"Footer %i", section];

}

Réagir aux actions sur la liste

Le développeur peut fournir un délégué à la liste d’éléments. Ce dernier doit implémenter le protocole UITableViewDelegate.

Le délégué autorise à personnaliser davantage l’affichage de la liste et aussi à réagir lorsque l’utilisateur sélectionne un élément, modifie une ligne, etc.

On peut réagir à l’action de l’utilisateur sur la liste en utilisant le mécanisme de transition inclus dans les storyboards, appelé segue.

Sélection d’un élément

Sélection à l’aide des storyboards

Pour la sélection à l’aide des segues, on commence par créer un nouveau ViewController (qu’on appelle par exemple DetailViewController) puis on connecte le nouveau ViewController au RootViewController. Pour cela, on fait un Control-clic depuis la cellule du RootViewController et on tire un trait vers le nouveau ViewController. Le segue ainsi créé propose un choix de transition. Il faut choisir Push car nous sommes dans un environnement de navigation. Puis, on sélectionne le segue et, dans l’Identity Inspector, on lui donne comme identifiant : PresenterDetail.

image

Figure 11–4 Association des ViewController dans le storyboard

Le nouveau ViewController doit alors être associé à la nouvelle classe. On clique sur le bandeau noir en bas, et, à droite, dans l’Identity Inspector, on choisit la classe dans la rubrique Custom Class.

image

Figure 11–5 Association du ViewController à sa classe

La méthode alors appelée lors de la sélection est la suivante.

Méthode de transition (segue) appelée lors de la sélection

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

/*
Lorsqu’une ligne est sélectionnée, la transition (segue) va créer le DetailViewController comme destination.
On peut alors allouer les éléments particuliers à ce nouveau ViewController si nécessaire en fonction de la ligne sélectionnée.
*/

NSLog(@"Appel à un segue.");

if ([[segue identifier] isEqualToString:@"PresenterDetail"]) {

NSIndexPath *selectedRowIndex =

[self.tableView1 indexPathForSelectedRow];

NSString *str =

[[NSString alloc] initWithFormat:@"%@",

[liste1 objectAtIndex:selectedRowIndex.row]];

DetailViewController_iPhone *viewController =

[segue destinationViewController];

viewController.nomAnimal = str;

}

}

Édition dans une TableView

Le mode édition d’une liste sert à l’utilisateur pour supprimer des éléments, en ajouter ou les réorganiser.

Pour activer le mode édition, on appelle la méthode setEditing:animated: sur l’objet tableView. Il faut ensuite implémenter les méthodes correspondantes du délégué pour effectivement supprimer les éléments et mettre la liste à jour.

Nous allons illustrer les différents modes proposés par le nouveau projet présenté plus loin.

Techniques pour afficher des cellules personnalisées

Nous avons vu comment afficher du texte dans une liste d’éléments. Le plus souvent, vous voudrez afficher des contenus plus riches, contenant des images, plusieurs lignes de texte, etc.

Il existe deux techniques différentes pour fournir des cellules propres à votre application. Chacune a ses avantages et ses inconvénients ; le choix de la technique à utiliser dépendra de votre projet.

Dans tous les cas, la hauteur de la cellule devra être indiquée à la table en définissant la propriété rowHeight (il est également possible d’avoir des cellules de tailles différentes grâce à la méthode tableView:heightForRowAtIndexPath: du délégué).

Composition de la cellule

La classe UITableViewCell hérite de UIView et on peut donc lui ajouter une hiérarchie de vues plus complexe. L’exemple suivant construit la cellule manuellement, mais il est possible de créer la cellule graphiquement dans le storyboard et de l’associer à des outlets présents dans une sous-classe de UITableViewCell.

Exemple de construction d’une cellule plus complexe

#define CELL_IDENTIFIER @"myCellIdentifier"

- (UITableViewCell *) tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

UITableViewCell *cell =

[tableView dequeueReusableCellWithIdentifier:CELL_IDENTIFIER];

UILabel *firstTextLabel;

UILabel *secondTextLabel;

UIImageView *imageView;

if (cell == nil)

{

cell =

[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault

reuseIdentifier:CELL_IDENTIFIER];

firstTextLabel =

[[UILabel alloc] initWithFrame:CGRectMake(50, 2, 200, 20)];

firstTextLabel.tag = 10;

[cell addSubview:firstTextLabel];

secondTextLabel =

[[UILabel alloc] initWithFrame:CGRectMake(50, 20, 200, 20)];

secondTextLabel.tag = 11;

[cell addSubview:secondTextLabel];

imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 40, 30)];

imageView.tag = 12;

[cell addSubview:imageView];

}

else {

firstTextLabel = (UILabel*)[cell viewWithTag:10];

secondTextLabel = (UILabel*)[cell viewWithTag:11];

imageView = (UIImageView*)[cell viewWithTag:12];

}

firstTextLabel.text =

[NSString stringWithFormat:@"Section: %u", indexPath.section];

secondTextLabel.text =

[NSString stringWithFormat:@"Ligne: %u", indexPath.row];

imageView.backgroundColor = [UIColor redColor];

return cell;

}

ASTUCE Utilisation des identifiants de vue

Toutes les vues peuvent recevoir un identifiant fourni par le développeur via la propriété tag. On retrouve ensuite cette vue dans une hiérarchie en appelant la méthode viewWithTag:.

Nous avons utilisé cette technique dans notre exemple pour retrouver les sous-vues qui ont été ajoutées manuellement lorsqu’on recycle la cellule.

Dessiner manuellement le contenu de la cellule

La dernière solution ne repose pas sur un assemblage de vues, mais sur une seule vue, dérivant de UITableViewCell et qui a en charge le dessin de l’ensemble de la cellule.

Pour implémenter cette solution, il faut utiliser les API de dessin 2D pour dessiner directement le texte et les images de la cellule, ce qui sort du cadre de ce livre, mais c’est de loin la solution la plus performante lorsque vous devez dessiner des cellules complexes contenant de nombreuses informations.

POUR APPROFONDIR Dessiner manuellement le contenu de la cellule

Le guide de programmation des listes d’éléments (TableView Programming Guide) fournit des exemples d’implémentation pour les deux techniques présentées ici.

Passage de données entre ViewControllers

L’un des problèmes rencontrés le plus fréquemment est le passage des données en avant et en arrière.

Comment passer le contenu de la cellule sélectionnée à un autre ViewController, par exemple comment dans notre liste d’animaux passer le nom de l’animal, de façon à obtenir plus de détails sur lui ?

Autre exemple, si nous voulons ajouter un nouvel animal, nous devons passer à un ViewController de saisie ; comment rafraîchir la liste des animaux lors du retour de ce ViewController ?

Tout se passe dans le mécanisme de transition appelé segue.

Pour le passage avant

En reprenant le projet précédent, on retrouve le passage de la chaîne de caractères dans la méthode prepareForSegue:sender :

Transition et passage avant de données

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@"PresenterDetail"]) {

// Récupérer l’index sélectionné

NSIndexPath *selectedRowIndex =

[self.tableView1 indexPathForSelectedRow];

// Récupérer la chaîne de caractères de la cellule

[liste1 objectAtIndex:selectedRowIndex.row]);

NSString *str =

[[NSString alloc] initWithFormat:@"%@",

[liste1 objectAtIndex:selectedRowIndex.row]];

// Récupérer le ViewController cible/destination

// Associer le pointeur sur la chaîne de caractères

// de ce ViewController à l’animal sélectionné

DetailViewController_iPhone *viewController =

[segue destinationViewController];

viewController.nomAnimal = str;

}

}

Lorsque la segue est invoquée, on récupère le ViewController destination et on accède à ses propriétés. On peut alors par exemple lui associer une chaîne de caractères qu’il utilisera librement.

Pour le passage arrière

Pour le passage arrière, il existait une méthode assez complexe avec l’utilisation des protocoles en Objective-C, mais avec iOS 6, nous disposons d’un mécanisme bien plus facile à utiliser : les unwind segues, des sortes de segues arrière.

Voyons comment les mettre en place.

Modification dans le .h du ViewController appelant

/…

/ Actions - unwind segue

-(IBAction)segueRetour:(UIStoryboardSegue *)sender;

Dans le ViewController appelant, ajoutez une nouvelle méthode de type IBAction et prenant comme argument un objet de type UIStoryboardSegue * :

Modification dans le .m du ViewController appelant

// Unwind segue - segue retour

-(IBAction)segueRetour:(UIStoryboardSegue *)sender {

// Rechargement d’un tableview par exemple

[self.laTableView1 reloadData];

}

Dans le ViewController qui déclenchera la segue pour revenir par exemple d’une navigation, implémentez la même action.

Modification du .m du ViewController appelé lors du retour de segue

#import "AjoutViewController.h"

#import "RootViewController.h"

@interface AjoutViewController ()

@end

@implementation AjoutViewController

#pragma mark - Méthodes segue

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@"unwindAjouter"]) {

RootViewController *destinationViewController =

[segue destinationViewController];

[destinationViewController.liste1 addObject:@"Canard"];

}

}

Dans le storyboard, associez un déclencheur (par exemple un bouton) à l’élément graphique intitulé exit dans le dock du ViewController. Une pop-up menu se manifeste avec le nom de la méthode devant être connectée.

image

Figure 11–6 Association d’une unwind segue

C’est une grosse amélioration par rapport aux versions précédentes d’iOS qui va beaucoup simplifier la vie du développeur. On peut revenir sur des méthodes appartenant à n’importe quel ViewController dans l’arborescence des scènes.

Un exemple complet

Nous allons détailler tout un projet pour mettre en œuvre la majorité des options couvertes dans ce chapitre. En résumé, la mise en place d’une liste d’éléments nécessite différentes étapes.

1 Créez une instance de UITableView et ajoutez-la dans la vue d’un contrôleur.

2 Ajoutez le protocole UITableViewSource au contrôleur.

3 Implémentez la méthode numberOfSectionsInTableView: pour indiquer le nombre de sections dans la liste d’éléments.

4 Implémentez la méthode tableView:numberOfRowsInSection: de la source de données pour déterminer le nombre de cellules à allouer.

5 Implémentez la méthode tableView:cellForRowAtIndexPath: de la source de données pour fournir les données aux cellules.

6 Éventuellement, ajoutez le protocole UITableViewDelegate et implémentez la méthode tableView:didSelectRowAtIndexPath:pour déclencher une action lorsque l’utilisateur sélectionne un élément.

Le code ci-après est un exemple complet d’un contrôleur de vue reproduisant l’illustration suivante. Il peut servir de base à vos expérimentations avec les listes d’éléments.

image

Figure 11–7 Le projet va présenter une liste d’éléments issus d’une NSMutableArray. Cette liste sera dans une seule section avec un en-tête et un pied de section.

Les états présentés seront proposés dans le bouton segmenté qui sera situé dans la barre de navigation et le bouton en haut à gauche de la barre de navigation nous permettra de revenir à l’affichage de la liste.

Création du projet

1 Créez un nouveau projet de type Empty Application. Appelez-le par exemple ProjetTableView.

2 Choisissez Use Automatic Reference Counting afin de profiter du système d’allocation et de désallocation fourni par le mécanisme ARC d’iOS 5 et Device Family : iPhone pour simplifier le code. En reprenant l’exercice, vous l’adapterez facilement à une interface iPad.

Création des ViewController

1 Créez un nouveau fichier (New File…) de type iOS > Cocoa Touch > UIViewController subclass et appelez-le RootViewController_iPhone.

2 Créez un second fichier (New File…) de même type et appelez-le DetailViewController_iPhone.

Création du storyboard et connexion avec les ViewController

1 Ajoutez un nouveau fichier (New File…) de type iOS > User Interface Storyboard et nommez-le : MainStoryboard_iPhone.

2 Connectez le storyboard à votre projet en effectuant les opérations suivantes.

1. Sélectionnez le projet (en bleu en haut de la partie navigation).

2. Sélectionnez Targets > Summary.

3. Dans la rubrique Summary, choisissez le nom du storyboard.

4. Dans la navigation, modifiez la méthode application:didFinishLaunchingWithOptions: pour ne pas surcharger le storyboard avec une nouvelle fenêtre (window).

La méthode : didFinishLaunchingWithOptions

- (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

// Départ de l’application.

return YES;

}

Le fichier RootViewController.h

Déclarez les éléments suivants :

1 un UITableView ;

2 une NSMutableArray qui contiendra la liste des animaux ;

3 un UISegmentedControl pour choisir les modes d’édition ;

4 un UIBarButtonItem pour revenir à la présentation de la liste ;

5 un entier de type NSInteger qui indiquera l’état dans lequel on se trouve ;

6 deux actions qui seront respectivement associées au bouton segmenté et au bouton de la barre de navigation.

Notez que tous les éléments sont définis comme des propriétés et les trois éléments graphiques (UI…) possèdent le mot-clé IBOutlet pour être visualisés dans le storyboard.

Pensez bien à associer les protocoles UITableViewDataSource et UITableViewDelegate à ce ViewController pour alimenter la TableView et réagir lors de son utilisation.

Exemple d’utilisation d’une cellule construite avec Interface Builder #import <UIKit/UIKit.h>

@interface RootViewController_iPhone : UIViewController

<UITableViewDelegate, UITableViewDataSource> {

// Le TableView

UITableView *tableView1;

// La liste

NSMutableArray *liste1;

// Le segmented control

UISegmentedControl *segmentedControl;

// Le BarButtonItem

UIBarButtonItem *barButtonItem;

// État - savoir dans quel état se trouve la liste/TableView

NSInteger etat; // 0 = déplacement

// 1 = Ajout

// 2 = Suppression

// 3 = neutre

}

@property (nonatomic,strong) IBOutlet UITableView *tableView1;

@property (nonatomic,strong) NSMutableArray *liste1;

@property (nonatomic,strong) IBOutlet

UISegmentedControl *segmentedControl;

@property (nonatomic,strong) IBOutlet UIBarButtonItem *barButtonItem;

@property (nonatomic) NSInteger etat;

// Action du segmented control

- (IBAction) clicSegmentedControl:(id)sender;

// Action du bar button item

- (IBAction) clicBarButtonItem:(id)sender;

@end

Connecter les propriétés au storyboard

Dans le storyboard, vous effectuerez les opérations suivantes.

1 Ajoutez un contrôleur de navigation depuis la partie Utilities (droite).

2 Ajoutez un nouveau ViewController que vous placerez à droite des deux premiers.

3 Ajoutez un bouton segmenté (segmented control) que vous placerez dans la barre de navigation du RootView Controller (au centre) et connectez-le à la référence d’outlet de bouton segmenté dans la partie connexion.

4 Ajoutez un BarButtonItem à droite de la barre de navigation du RootViewController (au centre) et connectez-le à la référence d’outlet.

5 Ajoutez un TableView (qui apparaît avec un TableViewCell). Lors de la connexion de ce TableView, n’oubliez pas de connecter le ViewController au TableView et le TableViewdelegate et datasource au ViewController.

6 Sélectionnez le RootViewController dans le résumé à gauche du storyboard et dans le panneau Utilities (à droite), le Connection Inspector (petite flèche). Vous devez voir apparaître les outlets que vous venez de déclarer dans le .h précédent.

7 Dans le nouveau ViewController (à droite), placez deux labels qui seront connectés ultérieurement.

Le résultat obtenu est le suivant.

image

Figure 11–8

NOTE

Notez bien les outlets qui apparaissent à droite et qui sont connectés, la bibliothèque d’objets graphiques en bas à droite et le résumé des différentes scènes à gauche.

Le fichier RootViewController.m

Nous allons détailler chaque partie de ce fichier qui est au cœur du projet.

Les propriétés

Le fichier commence avec ses en-têtes de déclaration et les propriétés « synthétisées ».

Notez l’importation du DetailViewController_iPhone.h qui sera appelé lors de la sélection d’une cellule.

Un exemple complet de contrôleur avec une liste d’éléments : TableViewController.m

#import "RootViewController_iPhone.h"

#import "DetailViewController_iPhone.h"

@implementation RootViewController_iPhone

#pragma mark - Propriétés

@synthesize tableView1;

@synthesize liste1;

@synthesize segmentedControl;

@synthesize barButtonItem;

@synthesize etat;

Méthodes associées aux boutons

La méthode associée au bouton segmenté (UISegmentedControl) reçoit l’index du bouton. En fonction de cet index, on change la valeur de l’état, le titre du bouton de la barre et on place le TableView en mode édition.

Le bouton segmenté est masqué pendant l’édition.

Méthode associée au segmented control :clicSegmentedControl:sender

// Actions changeant le mode d’édition du TableView.

- (IBAction)clicSegmentedControl:(id)sender {

// Action à effectuer en fonction du contrôle sélectionné

switch ([sender selectedSegmentIndex]) {

case 0:

// Déplacement

etat = 0;

self.barButtonItem.title = @"Fin";

[tableView1 setEditing:YES animated:YES];

self.title = @"Déplacement";

break;

case 1:

// Insertion

etat = 1;

self.barButtonItem.title = @"Fin";

[tableView1 setEditing:YES animated:YES];

self.title = @"Insertion";

break;

case 2:

// Suppression

etat = 2;

self.barButtonItem.title = @"Fin";

[tableView1 setEditing:YES animated:YES];

self.title = @"Suppression";

break;

case 3:

// État neutre

etat = 3;

break;

default:

break;

}

// Le segmented control est caché

self.segmentedControl.hidden = YES;

}

La méthode associée au BarButtonItem est appelée lors de la sortie du mode d’édition. Elle remet dans l’état 3 (neutre, présentation de la liste), remet le titre du bouton, fait réapparaître le bouton segmenté et sort le TableView du mode d’édition.

Méthode associée au BarButtonItem :clicBarButtonItem:sender

// Actions changeant le mode d’édition du TableView.

- (IBAction)clicBarButtonItem:(id)sender {

NSLog(@"Etat = %d\n", etat);

switch (etat) {

case 0:

case 1:

case 2:

self.barButtonItem.title = @"État";

etat = 3;

self.segmentedControl.selectedSegmentIndex = 3;

// Le segmented control est révélé à nouveau

self.segmentedControl.hidden = NO;

[tableView1 setEditing:NO animated:YES];

break;

case 3:

break;

}

}

Déplacer les cellules

Lorsque le TableView passe en édition, il appelle la méthode tableView:canMoveRowAtIndexPath: et place un indicateur de déplacement à droite de chaque cellule. On peut choisir quelles cellules seront déplaçables ou non.

Lors du déplacement des cellules dans la table, la méthode tableView:moveRowAtIndexPath:toIndexPath est appelée, la liste est réorganisée et le tableau représenté à l’écran.

Méthodes utilisées pour un déplacement de cellules : RootViewController.m

#pragma mark - Méthodes data source utilisées pour un déplacement de cellules

- (BOOL) tableView:(UITableView *)tableView

canMoveRowAtIndexPath:(NSIndexPath *)indexPath

{

BOOL resultat = NO;

if (self.etat == 0) {

resultat = YES;

}

return resultat;

}

- (void) tableView:(UITableView *)tableView

moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath

toIndexPath:(NSIndexPath *)destinationIndexPath

{

if (self.etat == 0) {

NSString *str= [liste1 objectAtIndex:sourceIndexPath.row];

[liste1 removeObjectAtIndex:sourceIndexPath.row];

[liste1 insertObject:str atIndex:destinationIndexPath.row];

}

}

Insérer ou supprimer une cellule

Lorsque la table passe en mode édition, elle appelle la méthode tableView editingStyleForRowAtIndexPath:, puis la méthode tableView commitEditingStyle: editingStyle forRowAtIndexPath:.

Dans cet exemple, l’insertion est directement effectuée sans interaction avec l’utilisateur. Il faudra ajouter un stockage persistant pour travailler avec une interaction plus poussée.

Le bouton de suppression apparaîtra directement et la méthode appelée s’occupera de supprimer la cellule dans la liste.

Méthodes appelées pour l’insertion : RootViewController.m

#pragma mark - Méthodes data source pour une insertion de cellules

- (void)insertion {

NSLog(@"Entrée dans la fonction : %s", __FUNCTION__);

// Ajout d’un élément dans la liste et rechargement du TableView

[liste1 addObject:@"Canard"];

[tableView1 reloadData];

}

- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView

editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath

{

NSInteger etatlocal = UITableViewCellEditingStyleNone;

switch (etat) {

case 0: // Déplacement

break;

case 1: // Ajout simple

etatlocal = UITableViewCellEditingStyleInsert;

break;

case 2: // Suppression

etatlocal = UITableViewCellEditingStyleDelete;

break;

case 3: // Neutre

break;

default:

break;

}

return etatlocal;

}

- (void) tableView:(UITableView *)tableView

commitEditingStyle:(UITableViewCellEditingStyle)editingStyle

forRowAtIndexPath:(NSIndexPath *)indexPath

{

if (editingStyle == UITableViewCellEditingStyleInsert) {

[self insertion];

}

if (editingStyle == UITableViewCellEditingStyleDelete){

if (self.liste1 != nil &&

indexPath.row < [self.liste1 count]){

/* Supprimer la source */

[self.liste1 removeObjectAtIndex:indexPath.row];

/* Puis supprimer la cellule de la TableView */

[tableView1 deleteRowsAtIndexPaths:[NSArray

arrayWithObject:indexPath]

withRowAnimation:UITableViewRowAnimationLeft];

}

}

}

Les méthodes de base d’un TableView

Pour un TableView, on a quasiment toujours besoin de faire appel aux méthodes pour gérer les sections, les lignes, les textes et la sélection de l’utilisateur.

Notez l’appel effectué au DetailViewController_iPhone à travers la segue (transition) et l’allocation de la NSString qui est associée à la propriété nomAnimal se trouvant dans le contrôleur de vue cible.

Méthodes de TableView : RootViewController.m

#pragma mark - Méthodes data source pour un tableView

// Nombre de sections de la TableView

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

return 1;

}

// Nombre de cellules pour chacune des sections

- (NSInteger) tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section

{

return [liste1 count];

}

// Personnalisation des cellules.

- (UITableViewCell *) tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString

*CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView

dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) { cell = [[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault

reuseIdentifier:CellIdentifier];

}

// Configuration de la cellule…

cell.textLabel.text = [NSString stringWithFormat:@"%@", [liste1

objectAtIndex:indexPath.row]];

[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];

return cell;}

#pragma mark - Méthodes optionnelles data source pour un tableView

// En-tête de section.

- (NSString *) tableView:(UITableView *)tableView

titleForHeaderInSection:(NSInteger)section {

return [NSString stringWithFormat:@"Header %i", section];

}

// Pied de section.

- (NSString *) tableView:(UITableView *)tableView

titleForFooterInSection:(NSInteger)section {

return [NSString stringWithFormat:@"Footer %i", section];

}

#pragma mark - Méthodes delegate pour un tableView

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@"PresenterDetail"]) {

NSIndexPath *selectedRowIndex =

[self.tableView1 indexPathForSelectedRow];

NSString *str =

[[NSString alloc] initWithFormat:@"%@",

[liste1 objectAtIndex:selectedRowIndex.row]];

DetailViewController_iPhone *viewController =

[segue destinationViewController];

viewController.nomAnimal = str;

}

}

Autres méthodes du RootViewController.m

Les autres méthodes sont celles apportées par le fichier. Dans la méthode viewDidLoad, on initialise le tableau et on se place dans l’état initial.

Autres méthodes : RootViewController.m

#pragma mark - Méthodes Apple

- (void)viewDidLoad

{

[super viewDidLoad];

liste1 = [[NSMutableArray alloc] init];

[liste1 addObject:@"Ara"];

[liste1 addObject:@"Boa"];

[liste1 addObject:@"Chat"];

[liste1 addObject:@"Daim"];

[liste1 addObject:@"Éléphant"];

[liste1 addObject:@"Faon"];

[liste1 addObject:@"Gorille"];

[liste1 addObject:@"Héron"];

[liste1 addObject:@"Iguane"];

[liste1 addObject:@"Jaguar"];

[liste1 addObject:@"Koala"];

[liste1 addObject:@"Lion"];

[liste1 addObject:@"Mouton"];

[liste1 addObject:@"Nandou"];

self.title = @"Liste";

self.segmentedControl.selectedSegmentIndex = 3;

etat = 3;

}

@end

Le fichier DetailViewController.h

Il contient uniquement le nom de l’animal sélectionné dans la liste qui pourra alors servir de référence pour une recherche dans une base de données, un fichier XML ou toute autre source de données.

Un exemple complet de contrôleur avec une liste d’éléments : TableViewController.h

#import <UIKit/UIKit.h>

@interface DetailViewController_iPhone : UIViewController {

UIView *maView;

UILabel *labelAnimal;

NSString *nomAnimal;

}

@property (nonatomic,strong) IBOutlet UIView *maView;

@property (nonatomic,strong) IBOutlet UILabel *labelAnimal;

@property (nonatomic,strong) NSString *nomAnimal;

@end

Le fichier DetailViewController.m

Notez l’initialisation du label à partir de la propriété nomAnimal allouée au moment de l’appel au ViewController.

Un exemple complet de contrôleur avec une liste d’éléments : TableViewController.h

#import "DetailViewController_iPhone.h"

@implementation DetailViewController_iPhone

#pragma mark - Propriétés

@synthesize maView;

@synthesize labelAnimal;

@synthesize nomAnimal;

#pragma mark - Méthodes Apple

- (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

}

#pragma mark - View lifecycle

- (void)viewDidLoad

{

[super viewDidLoad];

// Départ

self.labelAnimal.text = nomAnimal;

self.title = @"Animal";

}

@end

Conclusion

Dans ce chapitre, nous avons vu comment utiliser la classe UITableView pour créer des listes d’éléments de différents types et comment y intégrer des cellules spécifiques en fonction des besoins de votre application.

Cette classe est extrêmement importante, car les listes sont souvent le moyen le plus efficace pour présenter des informations et les utilisateurs les comprennent très bien.

Il est nécessaire de faire et refaire toutes les variations de cet exercice pour bien appréhender cette classe essentielle à la fois au format iPhone et au format iPad. Bientôt, vous effectuerez tous les exemples de ce chapitre les yeux fermés !

Propriété de Marie Richie <FB1245387-C111754-prod@customers.feedbooks.com>