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
).
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 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 exposent différents groupes de données qui peuvent être hétérogènes.
Chaque section de la liste est représentée comme un bloc à bords arrondis.
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.
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
:
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
.
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;
}
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] ;
}
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.).
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];
}
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.
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
.
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.
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;
}
}
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.
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é).
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.
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.
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.
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, 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.
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.
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.
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.
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.
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.
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;
}
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
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.
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.
Nous allons détailler chaque partie de ce fichier qui est au cœur du projet.
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;
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;
}
}
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];
}
}
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];
}
}
}
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;
}
}
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
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
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
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>