• 20 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

J'ai tout compris !

Mis à jour le 21/06/2013

Les informations tabulaires

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Les applications iOS ont souvent recours à des vues tabulaires pour présenter des listes de données. Ces listes permettent de :

  • présenter un ensemble d'options sélectionnables par l'utilisateur ;

  • naviguer dans un ensemble de données structurées hiérarchiquement ;

  • présenter une liste d'éléments triés selon un ou plusieurs critères ;

  • définir plusieurs niveaux de détail pour faciliter l'affichage des informations sur des devices de petite taille.

Ce chapitre va s'intéresser aux trois types de vues tabulaires utilisables sur un device : Table View, Picker View et Date Picker. Il va vous montrer comment afficher des contrôles tabulaires dans vos applications, comment les remplir et comment réagir aux actions des utilisateurs.

Xcode propose trois contrôles pour manipuler des informations tabulaires : Table View, Picker View et Date Picker :

  • le premier affiche des listes d'informations sur une colonne ;

  • le deuxième affiche des informations sur une ou plusieurs colonnes, sous la forme d'une roulette 3D ;

  • le troisième est très similaire au deuxième, à ceci près qu'il est spécialisé dans l'affichage des dates et d'heures.

Nous allons voir comment les utiliser dans ce chapitre.

Listes d'informations sur une colonne

Les contrôles Table View sont utilisés pour afficher des listes d'informations sur une colonne. Plusieurs applications fournies avec votre device utilisent un contrôle Table View (figure suivante).

L'application Contacts utilise un Table View

Les contrôles Table View peuvent être composés de zéro (même si cela n'offre pas beaucoup d'intérêt), une ou plusieurs sections. Chaque section peut :

  • comporter zéro, une ou plusieurs lignes ;

  • être précédée d'un en-tête de section ;

  • être suivie d'un pied de section.

Les sections sont identifiées par leur numéro d'index dans le tableau : première section, deuxième section, etc. Les lignes sont identifiées par leur numéro d'index dans une section : première ligne de la première section, deuxième ligne de la première section, etc.

L'utilisateur peut se déplacer verticalement dans un contrôle Table View en utilisant une gestuelle de glisser. Lorsqu'il clique sur un des éléments listés, une vue détaillée de cet élément est affichée. La communication entre le contrôle Table View et les différentes vues détaillées se fait via un contrôle Table View Controller.

Première approche

Pour faciliter l'écriture d'une application basée sur l'utilisation d'un Table View et des vues détaillées correspondantes, le plus simple consiste à utiliser le modèle Master-Detail Application.

Créez un nouveau projet basé sur le modèle Master-Detail Application, cliquez sur Next, donnez le nom « tableView » à l'application, choisissez le dossier de sauvegarde et validez en cliquant sur Create. Une fois l'application créée, cliquez sur MainStoryboard.storyboard dans le volet de navigation. Comme vous pouvez le voir à la figure suivante, un contrôleur de navigation, un contrôle Table View et une vue détaillée ont été insérés dans l'application.

Un contrôleur de navigation, un contrôle Table View et une vue détaillée ont été insérés dans l'application

Exécutez l'application en cliquant sur Run. Le contrôle Table View est bien visible, mais il ne contient aucune donnée. Nous allons remédier à cette situation en définissant quelques données textuelles et en les reliant au contrôle Table View.

La source de données peut provenir d'à peu près n'importe où : d'un tableau, d'un fichier XML ou d'une base de données par exemple. Pour que les choses soient aussi simples que possible, nous allons utiliser un tableau comme source de données. Cliquez sur MasterViewController.h dans le volet de navigation et définissez l'objet NSMutableArray *maListe :

#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *maListe;
}
@end

Maintenant, cliquez sur MasterViewController.m dans le volet de navigation. Pour que le tableau soit rempli dès le démarrage de l'application, nous allons ajouter quelques instructions dans la méthode viewDidLoad :

- (void)viewDidLoad
{
[super viewDidLoad];
maListe = [NSMutableArray array];
[maListe addObject:@"Paris"];
[maListe addObject:@"Lyon"];
[maListe addObject:@"Marseille"];
[maListe addObject:@"Toulouse"];
[maListe addObject:@"Nantes"];
[maListe addObject:@"Nice"];
[maListe addObject:@"Bordeaux"];
[maListe addObject:@"Montpellier"];
[maListe addObject:@"Rennes"];
[maListe addObject:@"Lille"];
[maListe addObject:@"Le Havre"];
[maListe addObject:@"Reims"];
[maListe addObject:@"Le Mans"];
[maListe addObject:@"Dijon"];
[maListe addObject:@"Grenoble"];
[maListe addObject:@"Brest"];
self.navigationItem.title = @"Grandes villes";
}

L'instruction de la ligne 4 initialise l'objet maListe :

maListe = [NSMutableArray array];

Les instructions suivantes ajoutent des éléments dans maListe en appliquant la méthode addObject à l'objet maListe. Par exemple :

[maListe addObject:@"Paris"];

Enfin, la ligne 21 définit le titre qui sera affiché sur la première ligne du contrôle Table View :

self.navigationItem.title = @"Grandes villes";

Je sens que vous brûlez d'impatience de voir ces données affichées dans le Table View. Allez, cliquez sur Run.

Oh consternation ! Le contrôle Table View est toujours vide !
Réfléchissez un peu. Tout ce qui a été fait jusqu'ici, c'est de créer un objet NSMutableArray. Comment le contrôle Table View pourrait-il supposer qu'il doit l'utiliser comme source de données ?

Pour cela, deux actions doivent être accomplies. Vous devez :

  1. indiquer au contrôle Table View combien de données il doit afficher ;

  2. relier les objets Table View et maListe.

Pour connaître le nombre d'éléments du tableau maListe, vous utiliserez l'instruction suivante :

[maListe count];

La méthode prédéfinie numberOfRowsInSection retourne un entier qui correspond au nombre d'éléments à afficher dans le Table View. Par défaut, la valeur retournée est égale à 0 : aucune information n'est donc affichée. En remplaçant le 0 à la suite de l'instruction return par le message [maListe count], le contrôle Table View sait combien d'éléments il doit afficher. Définissez la méthode suivante dans le fichier MasterViewController.m :

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [maListe count];
}

Il ne reste plus qu'à relier le Table View au contrôle maListe. Cette étape se fera grâce à la méthode cellForRowAtIndexPath. Consultez la documentation Apple pour avoir une idée du contenu de cette méthode (figure suivante).

La documentation

Les premières lignes (1) définissent un objet cell de classe UITableViewCell et le relient à une cellule du Table View. Nous les reprendrons telles quelles.

Les dernières lignes (2) sont propres à l'exemple pris par Apple, nous allons donc les modifier. Dans le cas qui nous préoccupe, les cellules (cell.textLabel.text) doivent être reliées aux éléments du Table View ([maListe objectAtIndex: …]) dont l'index correspond au numéro de ligne du Table View (indexPath.row). Voici le code complet de la méthode cellForRowAtIndexPath :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
// Configuration de la cellule
NSString *cellValue = [maListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}

Les lignes 1 à 8 ainsi que la ligne 13 sont issues de l'aide Apple.
La ligne 11 définit l'objet cellValue de classe NSString (NSString *cellValue) et y stocke l'élément du tableau maListe ([maListe …) qui se trouve à la ligne (objectAtIndex) courante (indexPath.row]).

NSString *cellValue = [maListe objectAtIndex:indexPath.row];

La ligne 12 affecte la valeur obtenue à la ligne précédente à la cellule :

cell.textLabel.text = cellValue;

Avant de cliquer sur Run, vous devez encore dire à Xcode que les données à afficher dans le Table View seront définies dans l'application. Comme à la figure suivante, cliquez sur MainStoryboard.storyboard dans le volet de navigation (1), cliquez sur Table View dans la zone d'édition (2), sur l'icône Hide or show the Utilities (3) puis sur l'icône Show the Attributes inspector (4). Choisissez enfin Dynamic Prototypes dans la liste déroulante Content (5).

Les données doivent être définies dans l'application

Maintenant, vous pouvez cliquer sur Run. La fenêtre du simulateur iOS devrait afficher quelque chose ressemblant à la figure suivante.

Les données apparaissent dans l'application

Une dernière petite chose. Avez-vous remarqué le point d'exclamation dans la partie supérieure de la fenêtre de Xcode ? Si vous cliquez dessus, un message d'erreur vous indique ce qui ne va pas. Ce dernier indique « Prototype table cells must have reuse identifiers », soit en bon français « Le prototype des cellules du tableau doit avoir un identifiant ».

Comme indiqué à la figure suivante, cliquez sur le prototype des cellules dans la vue Master (1), sur l'icône Show the Attributes inspector dans le volet des utilitaires (2), puis définissez un identifiant dans la zone de texte Identifier (3).

Définissez un identifiant dans la zone de texte Identifier

Utilisation de la vue détaillée

Les contrôles Table View sont généralement utilisés avec une vue secondaire qui donne des détails sur l'élément sélectionné par l'utilisateur dans la liste. Nous allons voir comment utiliser cette vue dans l'application précédente. Rappelez-vous : lors de la création de l'application tableView, une vue détaillée a été ajoutée au canevas (figure suivante). Reste donc à voir comment l'utiliser.

Une vue détaillée a été ajoutée au canevas

En observant cette figure, je vois que la vue Master n'est pas reliée à la vue Detail. Et pourtant, elle l'était tout à l'heure. Que s'est-il passé ?

Effectivement, ces deux vues étaient reliées, mais lorsque vous avez sélectionné Dynamic Prototypes dans la propriété Content de la vue Master, le lien a été brisé.
Qu'à cela ne tienne, nous allons le recréer !

Si ce n'est pas déjà fait, affichez le canevas en cliquant sur MainStoryboard.storyboard dans le volet de navigation. Ensuite, comme indiqué à la figure suivante, cliquez sur l'élément qui représente une cellule dans la vue Master (1) puis contrôle-glissez-déposez cet élément sur la vue Detail (2). Au relâchement du bouton gauche de la souris, sélectionnez Push dans le menu (3).

Il faut relier la vue Master à la vue Detail

La liaison entre les vues Master et Detail est maintenant rétablie (figure suivante).

La liaison entre les vues Master et Detail est maintenant rétablie

Pour pouvoir y faire référence dans le code, vous allez lui donner un nom. Comme indiqué sur la figure suivante, cliquez sur le symbole qui identifie la liaison dans le canevas (1), sur l'icône Hide or show the Utilities dans la barre d'outils (2), sur Show the Attributes inspector dans le volet des utilitaires (3) puis donnez le nom « detailSegue » à la liaison (4).

Il faut donner un nom à la liaison pour pouvoir y faire référence

Vous allez maintenant insérer un contrôle Label dans la vue Detail. Ce contrôle sera utilisé pour afficher des informations en relation avec l'élément sélectionné par l'utilisateur dans la vue Master.

Une fois le contrôle Label inséré dans la vue Detail, associez-lui l'outlet donneeRecue pour qu'il soit accessible dans le code.

Je pense que cette opération doit maintenant être habituelle pour vous, mais à tout hasard, je vais faire un rappel. Pour définir l'outlet unMessage pour le contrôle Label :

  1. dans la barre d'outils de Xcode, au-dessus du libellé Editor, cliquez sur l'icône Show the Assistant editor ;

  2. contrôle-glissez-déposez le contrôle Label dans le fichier d'en-têtes, juste au-dessus du @end final ;

  3. au relâchement du bouton gauche de la souris, donnez le nom « donneeRecue » à l'outlet et validez en cliquant sur Connect.

Le code du fichier d'en-têtes doit maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UILabel *donneeRecue;
@end

Lorsque l'utilisateur sélectionne un élément dans le contrôle Table View, la vue Detail remplace la vue Master. Pour passer des informations de la vue Master à la vue Detail, Apple préconise d'utiliser la méthode prepareForSegue. Malheureusement, il n'est pas possible d'atteindre directement le label donneeRecue, vous devez passer par une variable intermédiaire. Cliquez sur DetailViewController.h dans le volet de navigation et définissez la propriété texteAAfficher comme suit :

@property (strong, nonatomic) id texteAAfficher;

Le fichier DetailViewController.h doit maintenant contenir les instructions suivantes :

#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id texteAAfficher;
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UILabel *donneeRecue;
@end

Cliquez sur DetailViewController.m dans le volet de navigation et ajoutez une instruction synthesize pour pouvoir accéder à la propriété texteAAfficher :

@synthesize texteAAfficher = _texteAAfficher;

Ça y est, tout est en place pour insérer la méthode prepareForSegue. Cliquez sur MasterViewController.m dans le volet de navigation et insérez les instructions suivantes dans le code (peu importe l'endroit) :

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:@"detailSegue"])
{
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
DetailViewController *dvc = [segue destinationViewController];
dvc.texteAAfficher = [NSString stringWithFormat:@"%@", [maListe objectAtIndex:selectedIndex]];
}
}

Ne soyez pas impressionnés par l'apparente complexité du code : l'autocomplétion est votre amie. Ainsi par exemple, lorsque vous commencez à écrire les premières lettres de la méthode prepareForSegue, une bulle s'affiche sur l'écran, comme à la figure suivante. Appuyez simplement sur la touche Entrée pour recopier le texte proposé par l'autocomplétion.

Xcode propose l'autocomplétion

La ligne 2 teste si l'identifiant de la transition entre la vue Master et la vue Detail a bien pour nom « detailSegue » :

if([[segue identifier] isEqualToString:@"detailSegue"])

La ligne 4 définit le NSInteger selectedIndex et l'initialise avec l'index de la cellule choisie par l'utilisateur (indexPathForSelectedRow) du contrôle TableView de la vue Master (self.tableView) :

NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];

selectedIndex vaudra 0 si l'utilisateur a choisi le premier élément. Il vaudra 1 s'il a choisi le deuxième élément. Ainsi de suite…

Maintenant, nous connaissons l'index de la cellule choisie par l'utilisateur. Encore faut-il accéder au Label de la vue Detail.
Pour cela, la ligne 5 définit l'objet dvc de classe DetailViewController (DetailViewController *dvc) et l'initialise avec la vue qui va être affichée ([segue destinationViewController]) :

DetailViewController *dvc = [segue destinationViewController];

Il ne reste plus qu'à affecter le contenu de la cellule à la propriété texteAAfficher. C'est précisément ce que fait la ligne 6. La propriété texteAAfficher est initialisée avec l'élément du tableau maListe d'index selectedIndex. Cet objet est converti en un NSString avant d'être stocké dans la propriété texteAAfficher :

dvc.texteAAfficher = [NSString stringWithFormat:@"%@", [maListe objectAtIndex:selectedIndex]];

Mais au fait, comment le code de la vue Master va-t-il pouvoir accéder au code de la vue Detail ?

Vous devez ajouter une instruction import au début du fichier MasterViewController.m, sans quoi Master n'arrivera pas à « dialoguer » avec Detail et plusieurs erreurs apparaîtront dans le code :

#import "DetailViewController.h"

Après tous ces efforts, vous devez certainement être impatients de lancer l'application. Vous pouvez toujours le faire, mais je suis assez pessimiste quant au résultat obtenu : il est vrai que la vue Master a indiqué à la vue Detail quelle était la cellule sélectionnée par l'utilisateur. Mais rappelez-vous, cette indication a été fournie dans la propriété texteAAfficher et non dans le Label donneeRecue. Vous allez donc devoir agir sur le code de la vue Detail. Cliquez sur DetailViewController.m et ajoutez la ligne 6 à la méthode viewDidLoad :

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
_donneeRecue.text = _texteAAfficher;
}

Cette ligne recopie simplement le contenu de la propriété texteAAfficher dans le Label.

Ça y est, vous pouvez lancer l'application et profiter du passage de données entre les vues Master et Detail. La figure suivante représente par exemple ce que vous devriez obtenir si vous choisissez « Toulouse » dans la vue Master.

« Toulouse  » est sélectionné dans la vue Master

Les fichiers de cette application se trouvent dans le dossier tableView2.

MasterViewController.h
#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *maListe;
}
@end
MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
@implementation MasterViewController
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
maListe = [NSMutableArray array];
[maListe addObject:@"Paris"];
[maListe addObject:@"Lyon"];
[maListe addObject:@"Marseille"];
[maListe addObject:@"Toulouse"];
[maListe addObject:@"Nantes"];
[maListe addObject:@"Nice"];
[maListe addObject:@"Bordeaux"];
[maListe addObject:@"Montpellier"];
[maListe addObject:@"Rennes"];
[maListe addObject:@"Lille"];
[maListe addObject:@"Le Havre"];
[maListe addObject:@"Reims"];
[maListe addObject:@"Le Mans"];
[maListe addObject:@"Dijon"];
[maListe addObject:@"Grenoble"];
[maListe addObject:@"Brest"];
self.navigationItem.title = @"Grandes villes";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [maListe count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//même que paramètre Identifier du Prototype Cells dans MainStoryboard.storyboard
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
// Configuration de la cellule
NSString *cellValue = [maListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:@"detailSegue"])
{
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
DetailViewController *dvc = [segue destinationViewController];
dvc.texteAAfficher = [NSString stringWithFormat:@"%@", [maListe objectAtIndex:selectedIndex]];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
@end
DetailViewController.h
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id texteAAfficher;
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UILabel *donneeRecue;
@end
DetailViewController.m
#import "DetailViewController.h"
@interface DetailViewController ()
- (void)configureView;
@end
@implementation DetailViewController
@synthesize texteAAfficher = _texteAAfficher;
@synthesize detailItem = _detailItem;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;
@synthesize donneeRecue = _donneeRecue;
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
_donneeRecue.text = _texteAAfficher;
}
- (void)viewDidUnload
{
[self setDonneeRecue:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end

Listes d'informations sur une ou plusieurs colonnes

Les contrôles Table View ont une variante : les Picker View. Outre leur aspect « roulette 3D », ces contrôles peuvent afficher des informations sur une ou plusieurs colonnes, et chaque colonne peut comporter un nombre d'informations différent.

Pour effectuer une sélection, l'utilisateur tourne la ou les roues (figure suivante) jusqu'à ce que la ou les valeurs affichées se trouvent sous la marque de sélection. Les valeurs sélectionnées sont obtenues avec la méthode pickerView:didSelectRow:inComponent.

Image utilisateur
Image utilisateur

Ces contrôles relèvent de la classe UIPickerView. Pour illustrer leur fonctionnement, nous allons définir une application dans laquelle l'utilisateur pourra sélectionner une entrée dans un contrôle Picker View. Cette dernière sera alors affichée dans un contrôle Label.

Définissez une nouvelle application basée sur le modèle Single View Application et donnez-lui le nom « picker ». En utilisant Interface Builder, ajoutez un contrôle Picker View et un contrôle Label au fichier MainStoryboard.storyboard. Double-cliquez sur le contrôle Label et tapez « Choisissez une ville ». L'interface de l'application doit maintenant ressembler à la figure suivante.

L'interface de l'application

Reliez les deux contrôles de l'interface au code en les contrôle-glissant-déposant depuis la zone d'édition dans le fichier ViewController.h. Définissez un outlet pour le contrôle Picker View et donnez-lui le nom pv. Définissez un outlet pour le contrôle Label et donnez-lui le nom status. Le fichier ViewController.h doit maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *pv;
@property (weak, nonatomic) IBOutlet UILabel *status;
@end

Pour utiliser un contrôle Picker View, il est nécessaire d'implémenter les protocoles UIPickerViewDelegate (traitement des actions de l'utilisateur dans le Picker View) et UIPickerViewDataSource (données utilisées dans le Picker View).

Mais au fait, qu'est-ce que ça veut dire « implémenter un protocole » ? J'ai un trou de mémoire !

Dans le jargon Objective-C, un protocole consiste en une liste de déclarations de méthodes. Lorsqu'une classe implémente un protocole, cela signifie qu'elle peut appeler les méthodes qui sont définies dans ce protocole.
Supposons par exemple que la classe Voiture, sous-classe de Vehicule, implémente le protocole Moteur. Pour le signifier à Xcode, vous devrez modifier l'instruction @interface.

@interface Voiture : Vehicule {
...
}

Comme ceci :

@interface Voiture : Vehicule <Moteur>{
...
}

Ici, la classe ViewController, sous-classe de UIViewController, doit implémenter les protocoles UIPickerViewDelegate et UIPickerViewDataSource. La déclaration de l'interface :

@interface ViewController : UIViewController {
...
}

doit donc être modifiée comme suit :

@interface ViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
...
}

Si ceci n'est pas clair, relisez calmement les quelques phrases qui précèdent. Vous verrez, il n'y a rien de sorcier !

Les données affichées dans le contrôle Picker View vont être stockées dans un tableau que nous appellerons maListe. Vous devez définir ce tableau dans le fichier d'en-têtes, qui devrait maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>
{
NSMutableArray *maListe;
}
@property (weak, nonatomic) IBOutlet UIView *pv;
@property (weak, nonatomic) IBOutlet UILabel *status;
@end

L'alimentation et la gestion du contrôle Picker View seront réalisées dans le code de l'application (ViewController.m). Pour l'indiquer à Xcode, il suffit de relier les outlets dataSource et delegate du Picker View au File's Owner (figure suivante).

  1. Cliquez sur MainStoryboard.storyboard dans le volet de navigation (1).

  2. Cliquez sur le contrôle Picker View dans le volet de navigation (2).

  3. Affichez le volet des utilitaires en cliquant sur l'icône Hide or show the Utilities (3).

  4. Cliquez sur Show the Connections inspector dans le volet des utilitaires (4).

  5. Sous Outlets, reliez le cercle qui se trouve à droite de dataSource à l'icône View Controller (5).

  6. Toujours sous Outlets, reliez le cercle qui se trouve à droite de delegate à l'icône View Controller (6).

Il faut relier les outlets dataSource et delegate du Picker View au File's Owner

Il est temps de modifier le fichier .m. Cliquez sur ViewController.m dans le volet de navigation.

Le tableau sera initialisé dans la méthode viewDidLoad :

- (void)viewDidLoad
{
[super viewDidLoad];
maListe = [[NSMutableArray alloc] init];
[maListe addObject:@"Paris"];
[maListe addObject:@"Lyon"];
[maListe addObject:@"Marseille"];
[maListe addObject:@"Toulouse"];
[maListe addObject:@"Nantes"];
[maListe addObject:@"Nice"];
[maListe addObject:@"Bordeaux"];
[maListe addObject:@"Montpellier"];
[maListe addObject:@"Rennes"];
[maListe addObject:@"Lille"];
[maListe addObject:@"Le Havre"];
[maListe addObject:@"Reims"];
[maListe addObject:@"Le Mans"];
[maListe addObject:@"Dijon"];
[maListe addObject:@"Grenoble"];
[maListe addObject:@"Brest"];
}

Il n'y a rien de bien compliqué dans ces instructions. L'objet maListe est instancié :

maListe = [[NSMutableArray alloc] init];

Puis plusieurs valeurs sont mémorisées dans le tableau à l'aide de la méthode addObject :

[maListe addObject:@"Paris"];

Nous allons maintenant mettre en place les méthodes qui vont « alimenter » le contrôle Picker View :

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [maListe count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [maListe objectAtIndex:row];
}

La valeur retournée par la méthode numberOfComponentsInPickerView définit le nombre de colonnes dans le Picker View. Ici, une seule colonne :

return 1;

La valeur retournée par la méthode numberOfRowsInComponent définit le nombre d'éléments affichés dans le Picker View. En ce qui nous concerne, ce nombre est égal au nombre d'éléments stockés dans le tableau maListe :

return [maListe count];

Enfin, la valeur retournée par la méthode titleForRow est affectée à l'élément d'index row. Il suffit de l'extraire du tableau maListevia la méthode objectAtIndex :

return [maListe objectAtIndex:row];

Pour terminer, nous allons définir la méthode qui va mettre à jour le Label lorsqu'un élément du Picker View sera sélectionné :

-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component{
status.text = [maListe objectAtIndex:row];
}

Lorsque l'utilisateur clique sur une entrée, le NSInteger row contient l'index de cette entrée. Pour retrouver la valeur correspondante, il suffit donc de la lire dans le tableau maListe ([maListe objectAtIndex:row]). Cette valeur est alors copiée dans la composante text du Label (status.text =). En d'autres termes, elle est affichée dans le Label.

Le code de cette application se trouve dans le dossier picker.

ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>
{
NSMutableArray *maListe;
}
@property (weak, nonatomic) IBOutlet UIView *pv;
@property (weak, nonatomic) IBOutlet UILabel *status;
@end
ViewController.m
#import "ViewController.h"
@implementation ViewController
@synthesize pv;
@synthesize status;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
maListe = [[NSMutableArray alloc] init];
[maListe addObject:@"Paris"];
[maListe addObject:@"Lyon"];
[maListe addObject:@"Marseille"];
[maListe addObject:@"Toulouse"];
[maListe addObject:@"Nantes"];
[maListe addObject:@"Nice"];
[maListe addObject:@"Bordeaux"];
[maListe addObject:@"Montpellier"];
[maListe addObject:@"Rennes"];
[maListe addObject:@"Lille"];
[maListe addObject:@"Le Havre"];
[maListe addObject:@"Reims"];
[maListe addObject:@"Le Mans"];
[maListe addObject:@"Dijon"];
[maListe addObject:@"Grenoble"];
[maListe addObject:@"Brest"];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [maListe count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [maListe objectAtIndex:row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component{
status.text = [maListe objectAtIndex:row];
}
- (void)viewDidUnload
{
[self setPv:nil];
[self setStatus:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end

Sélection d'une date dans un contrôle spécialisé

Le contrôle Date Picker est très proche du contrôle Picker View, à ceci près qu'il est spécialisé dans la sélection de dates et d'heures. Sa mise en œuvre est également plus simple que celle d'un Picker View.

À titre d'exemple, nous allons définir une application qui permettra à l'utilisateur de sélectionner une date et une heure dans un contrôle Date Picker. Un clic sur le bouton Sélectionner provoquera l'affichage de la sélection dans un contrôle Label, comme à la figure suivante.

Voici à quoi ressemblera notre application

Rien de très original me direz-vous ! Cependant, cette petite application va aborder les points essentiels concernant le contrôle Date Picker. Allez, retroussez vos manches !

Définissez une nouvelle application basée sur le modèle Single View Application et donnez-lui le nom « datePicker ». Sélectionnez le fichier MainStoryboard.storyboard et ajoutez-y un contrôle Date Picker, un contrôle Round Rect Button et un contrôle Label en utilisant Interface Builder. Double-cliquez sur le bouton et tapez « Sélectionner ». Double-cliquez sur le Label et tapez « Choisissez une date » puis appuyez sur Sélectionner. Enfin, positionnez ces trois contrôles pour obtenir quelque chose s'approchant de la figure suivante.

Positionnez les contrôles comme ceci

Cliquez sur l'icône Show the Assistant editor dans la barre d'outils de Xcode et définissez les outlets et actions suivants :

Contrôle

Outlet ou Action

Nom

UIDatePicker

Outlet

dp

UILabel

Outlet

status

UIButton

Action

selectionner

Le fichier ViewController.h doit maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIDatePicker *dp;
@property (weak, nonatomic) IBOutlet UILabel *status;
- (IBAction)selectionner:(id)sender;
@end

Rien de bien méchant là-dedans. Maintenant, vous devez être habitués à définir des interfaces avec Interface Builder et à référencer les contrôles dans le code en les contrôle-glissant-déposant depuis la zone d'édition sur le fichier d'en-têtes.

Si vous le souhaitez, vous pouvez dès à présent lancer l'application en cliquant sur Run. Le contrôle Date Picker est opérationnel, même si aucune instruction n'a encore été écrite dans le fichier .m.

Pour terminer l'application, nous allons donner vie au bouton en écrivant quelques lignes de code. Cliquez sur datePickerViewController.m dans le volet de navigation et complétez la méthode selectionner comme suit :

- (IBAction)selectionner:(id)sender {
NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];
NSDate *pickerDate = [dp date];
NSString *select = [[NSString alloc] initWithFormat:@"%@", [pickerDate descriptionWithLocale:frLocale]];
status.text = select;
}

Examinons ces instructions.
Dans un premier temps, l'objet frLocale de type NSLocale est défini (NSLocale *frLocale = [[[NSLocale alloc] …) puis initialisé avec les paramètres régionaux fr_FR (initWithLocaleIdentifier:@"fr_FR") :

NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];

Cet objet sera utilisé par la suite pour définir le format de la date et de l'heure à afficher dans le Label. Ici, nous avons utilisé le format fr_FR pour que les dates et heures soient affichées « à la française ».

Pour faciliter l'écriture, l'objet NSDate pickerDate est défini et initialisé avec la date sélectionnée par l'utilisateur dans le contrôle Date Picker :

NSDate *pickerDate = [dp date];

L'instruction suivante définit l'objet select de classe NSString et l'initialise avec la date sélectionnée par l'utilisateur (pickerDate) mise en forme selon les conventions françaises (descriptionWithLocale:frLocale) :

NSString *select = [[NSString alloc] initWithFormat:@"%@", [pickerDate descriptionWithLocale:frLocale]];

Enfin, la date et l'heure mémorisées dans l'objet select sont affectées à la composante text du Label, et donc, affichées dans le Label :

status.text = select;

Il faut bien l'avouer, la mise en œuvre d'un contrôle Date Picker est vraiment simple !

En résumé

  • Les contrôles Table View sont utilisés pour afficher des listes d'informations sur une colonne. Pour faciliter l'écriture d'une application basée sur l'utilisation d'un Table View et des vues détaillées correspondantes, le plus simple consiste à utiliser le modèle Master-Detail Application.

  • Pour relier les éléments de la vue Master à la vue Detail, il suffit de créer un Segue (Push, Modal ou Custom) en contrôle-glissant-déposant une cellule de la vue Master sur la vue Detail dans le canevas.

  • Les contrôles Table View ont une variante : les Picker View. Outre leur aspect « roulette 3D », ces contrôles peuvent afficher des informations sur une ou plusieurs colonnes, et chaque colonne peut comporter un nombre d'informations différent.

  • Le contrôle Date Picker est très proche du contrôle Picker View, à ceci près qu'il est spécialisé dans la sélection de dates et d'heures. Sa mise en œuvre est également plus simple que celle d'un Picker View.

Exemple de certificat de réussite
Exemple de certificat de réussite