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).

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.

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 :
indiquer au contrôle
Table View
combien de données il doit afficher ;relier les objets
Table View
etmaListe
.
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).

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).

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

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).

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.

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).

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

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).

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
:
dans la barre d'outils de Xcode, au-dessus du libellé
Editor
, cliquez sur l'icôneShow the Assistant editor
;contrôle-glissez-déposez le contrôle
Label
dans le fichier d'en-têtes, juste au-dessus du@end
final ;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.

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
.

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
.


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.

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).
Cliquez sur
MainStoryboard.storyboard
dans le volet de navigation (1).Cliquez sur le contrôle
Picker View
dans le volet de navigation (2).Affichez le volet des utilitaires en cliquant sur l'icône
Hide or show the Utilities
(3).Cliquez sur
Show the Connections inspector
dans le volet des utilitaires (4).Sous
Outlets
, reliez le cercle qui se trouve à droite dedataSource
à l'icôneView Controller
(5).Toujours sous
Outlets
, reliez le cercle qui se trouve à droite dedelegate
à l'icôneView Controller
(6).

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 maListe
via 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.

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.

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'unTable View
et des vues détaillées correspondantes, le plus simple consiste à utiliser le modèleMaster-Detail Application
.Pour relier les éléments de la vue
Master
à la vueDetail
, il suffit de créer unSegue
(Push
,Modal
ouCustom
) en contrôle-glissant-déposant une cellule de la vueMaster
sur la vueDetail
dans le canevas.Les contrôles
Table View
ont une variante : lesPicker 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ôlePicker 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'unPicker View
.