Ce chapitre s'intéresse aux contrôles et aux modèles de conception utilisés pour gérer les différentes vues d'une application. Au fil des pages, vous apprendrez à définir des tabulations, à mettre en place une barre de navigation, une barre d'outils et une barre de recherche, mais aussi à gérer deux vues simultanément sur un iPad, comme dans l'application de messagerie Mail, fournie en standard avec les iPad 1 et 2.
À la fin du chapitre, vous aurez une plus grande maîtrise du storyboard (l'outil de conception des vues de Xcode). Vous verrez à quel point il peut simplifier (voire même automatiser dans certains cas) la définition des vues et de leurs interconnexions.
Cette version de Xcode marque clairement le début d'une nouvelle ère dans la conception des applications. À présent, le programmeur passe un peu plus de temps à peaufiner son interface et un peu moins de temps à aligner du code. Et de nombreux blocs de code sont générés automatiquement. Qui s'en plaindrait ?
Applications multivues
Les contrôles Tab Bar
permettent de créer facilement des applications multivues. L'utilisateur passe d'une vue à l'autre en cliquant sur les icônes du contrôle Tab Bar
.

Les contrôles Tab Bar
permettent de créer facilement des applications multivues. L'utilisateur passe d'une vue à l'autre en cliquant sur les icônes du contrôle Tab Bar
, comme le montre la figure suivante.

Les contrôles Tab Bar
sont rattachés à la classe UITabBar
. Nous allons voir comment les mettre en place en développant une petite application contenant trois vues.
Commencez par définir une application basée sur le modèle Tabbed Application
et donnez-lui le nom « tabBar ». Cliquez sur MainStoryboard.storyboard
dans le volet de navigation. La figure suivante montre comment se présente le canevas.

Comme vous pouvez le voir:
un contrôleur
Tab Bar
et deux contrôleurs de vues ont automatiquement été insérés dans le canevas ;des liaisons entre le contrôleur
Tab Bar
et les contrôleurs de vues ont été mises en place ;un contrôle
Tab Bar
a été inséré dans la partie inférieure de la vue principale (Tab Bar Controller
) ;les deux vues secondaires contiennent plusieurs contrôles.
L'application est déjà opérationnelle (figure suivante). Juste pour vous faire plaisir, cliquez sur Run
et amusez-vous avec les différentes vues qui ont automatiquement été mises en place lors de la création de l'application.

Cette application comporte deux vues. Pour vous amuser, je vous propose d'insérer une troisième vue et de la relier au Tab Bar Controller
, comme indiqué à la figure suivante.
Glissez-déposez un
View Controller
de la bibliothèque d'objets sur le canevas (1).Maintenez la touche Ctrl enfoncée, cliquez sur le
Tab Bar Controller
, maintenez le bouton gauche de la souris enfoncé et dirigez son pointeur sur la nouvelle vue (2).Relâchez la touche Ctrl et le bouton de la souris et sélectionnez
Relationship - viewControllers
dans le menu (3).

Ça y est : vous venez d'insérer une troisième vue dans le projet et de la relier au Tab Bar Controller
.
Je sens que vous avez du mal à me croire. Allez, cliquez sur Run
et admirez le résultat (figure suivante).

Vous allez maintenant modifier les icônes des trois vues. Pour cela, vous aurez besoin d'icônes de 32x32 pixels. Vous trouverez sans peine de telles icônes sur le Web en tapant « icônes 32x32 » dans votre moteur de recherche préféré. Définissez un dossier « ressources » et ajoutez-y trois icônes de votre choix.
Vous pouvez également télécharger les icônes que j'ai utilisées (figure suivante) pour vous faciliter la tâche, mais rien ne vous empêche de créer les vôtres.

Pour modifier l'apparence d'un Tab Bar Item
, vous allez utiliser l'inspecteur des attributs.
Comme indiqué à la figure suivante, cliquez sur l'icône Hide or show the Utilities
(1) pour faire apparaître le volet des utilitaires, puis cliquez sur l'icône Show the Attributes inspector
(2). L'inspecteur des attributs étant affiché, cliquez sur le Tab Bar Item
de la première vue (3), et modifiez les paramètres Title
et Image
, sous Bar Item
(4).

Recommencez cette manipulation pour modifier les deux autres Tab Bar Item
.
Maintenant, il ne vous reste plus qu'à insérer le contenu souhaité dans les trois vues pour finaliser l'application. Je suis sûr que cela ne vous posera aucun problème.
Le code de l'application ne sera pas listé ici, car aucun code n'a été écrit. Je ne sais pas ce que vous en pensez, mais moi, je tire mon chapeau aux ingénieurs qui ont conçu cette nouvelle version de Xcode. On peut dire qu'elle facilite vraiment les choses. Ceux et celles qui ont connu les versions précédentes ne me contrediront certainement pas !
Les contrôles Tab Bar
sont rattachés à la classe UITabBar
. Nous allons voir comment le mettre en œuvre en développant une petite application contenant trois vues.
Définissez une nouvelle application basée sur le modèle Tabbed Application
et donnez-lui le nom "tabBar". Cliquez sur MainStoryboard.storyboard
dans le volet de navigation. Voici comment se présente le canevas :

Comme vous pouvez le voir:
un contrôleur
Tab Bar
et deux contrôleurs de vues ont automatiquement été insérés dans le canevas ;des liaisons entre le contrôleur
Tab Bar
et les contrôleurs de vues ont été mises en place ;un contrôle
Tab Bar
a été inséré dans la partie inférieure de la vue principale (Tab Bar Controller
) ;les deux vues secondaires contiennent plusieurs contrôles.
L'application est déjà opérationnelle. Juste pour vous faire plaisir, cliquez sur Run
et amusez-vous avec les différentes vues qui ont automatiquement été mises en places lors de la création de l'application.

Cette application comporte deux vues. Pour vous amuser, je vous propose d'insérer une troisième vue et de la relier au Tab Bar Controller
.
Glissez-déposez un
View Controller
de la bibliothèque d'objets sur le canevas (1).Maintenez la touche Contrôle en foncée, cliquez sur le
Tab Bar Controller
, maintenez le bouton gauche de la souris enfoncé et dirigez le pointeur de la souris sur la nouvelle vue (2).Relâchez la touche Contrôle et le pointeur de la souris et sélectionnez
Relationship - viewControllers
dans le menu (3).

Ca y est : vous venez d'insérer une troisième vue dans le projet et de relier cette troisième vue au Tab Bar Controller
.
Je sens que vous avez du mal à me croire. Allez, cliquez sur Run
et admirez le résultat :

Vous allez maintenant modifier les icônes des trois vues. Pour cela, vous aurez besoin d'icônes de 32x32 pixels. Vous trouverez sans peine de telles icônes sur le Web en tapant "icônes 32x32" dans votre moteur de recherche préféré. Définissez un dossier "ressources" et ajoutez-y trois icônes de votre choix. A titre d'information, voici les icônes que j'ai utilisées dans mon application :

Vous pouvez télécharger les icônes que j'ai utilisées pour vous faciliter la tâche, mais rien ne vous empêche de créer les vôtres.
Pour modifier l'apparence d'un Tab Bar Item
, vous allez utiliser l'inspecteur des attributs.
Cliquez si nécessaire sur l'icône Hide or show the Utilities
(1) pour faire apparaître le volet des utilitaires, puis cliquez sur l'icône Show the Attributes inspector
(2). L'inspecteur des attributs étant affiché, cliquez sur le Tab Bar Item
de la première vue (3), et modifiez les paramètres Title
et Image
, sous Bar Item
(4).

Recommencez cette manipulation pour modifier les deux autres Tab Bar Item
.
Et maintenant, il ne vous reste plus qu'à insérer le contenu souhaité dans les trois vues pour finaliser l'application. Je suis sûr que cela ne vous posera aucun problème ;)
Cette application se trouve dans le dossier tabBar.
Le code de l'application ne sera pas listé ici car ... aucun code n'a été écrit. Je ne sais pas ce que vous en pensez, mais moi, je tire mon chapeau aux ingénieurs qui ont conçu cette nouvelle version de Xcode. On peut dire qu'elle facilite vraiment les choses. Ceux et celles qui ont connu les versions précédentes ne me contrediront certainement pas !
Barre d'outils
Vous utiliserez un contrôle Tool Bar
chaque fois qu'il est nécessaire d'ajouter une barre d'outils dans une application. Ces contrôles relèvent de la classe UIToolBar
. Ils peuvent être composés d'un ou de plusieurs boutons rattachés à la classe UIBarButtonItem
. Ces boutons peuvent contenir du texte et/ou une image.
Pour vous entraîner à manipuler les contrôles Tool Bar
, vous allez définir une application dans laquelle quatre boutons permettront de changer la couleur et l'alignement du texte affiché dans un Label
. À la figure suivante se trouve le résultat à obtenir.

Créez une nouvelle application de type Single View Application
et donnez-lui le nom « toolBar ». Cliquez sur l'entrée MainStoryboard.storyboard
dans le volet de navigation. En utilisant Interface Builder, ajoutez un contrôle Tool Bar
à l'application et positionnez-le tout en bas de la fenêtre. Ajoutez trois Bar Button Items
au Tool Bar
pour obtenir quelque chose approchant de la figure suivante.

Double-cliquez successivement sur chacun des quatre Bar Button Items
et affectez-leur les libellés suivants : « Noir », « Rouge », « Gauche » et « Centre ».
Ajoutez un contrôle Label
à l'application. Affectez-lui le texte « Un simple Label pour illustrer le fonctionnement du contrôle Tool Bar ». Redimensionnez ce contrôle pour qu'il tienne sur plusieurs lignes et affectez la valeur 3 au paramètre Lines
.
Pour que le code puisse interagir avec les contrôles déposés sur l'application, vous allez définir des actions et un outlet.
Cliquez sur l'icône Show the Assistant Editor
et contrôle-glissez-déposez successivement les quatre Bar Button Items
juste au-dessus du @end
final dans le fichier d'en-têtes. Définissez les actions noir
, rouge
, gauche
et centre
.
Définissez l'outlet leLabel
pour le contrôle Label
.
Le fichier ViewController.h
doit maintenant ressembler à ceci :
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
- (IBAction)noir:(id)sender;
- (IBAction)rouge:(id)sender;
- (IBAction)gauche:(id)sender;
- (IBAction)centre:(id)sender;
@property (weak, nonatomic) IBOutlet UILabel *leLabel;
@end
Il ne reste plus qu'à écrire quelques lignes de code pour réagir aux appuis sur les Bar Button Items
.
Cliquez sur ViewController.m
dans le volet de navigation. Repérez les méthodes action et complétez-les comme ceci :
- (IBAction)noir:(id)sender {
leLabel.textColor = [UIColor blackColor];
}
- (IBAction)rouge:(id)sender {
leLabel.textColor = [UIColor redColor];
}
- (IBAction)gauche:(id)sender {
leLabel.textAlignment = UITextAlignmentLeft;
}
- (IBAction)centre:(id)sender {
leLabel.textAlignment = UITextAlignmentCenter;
}
Barre de recherche
Le contrôle Search Bar
est très pratique lorsqu'il est nécessaire d'effectuer une recherche dans une application. Ce contrôle relève de la classe UISearchBar
. Je vais vous montrer comment l'utiliser pour filtrer les données affichées dans un Table View
. À la figure suivante se trouve le résultat à obtenir.

Définissez une nouvelle application basée sur le modèle Master-Detail Application
et donnez-lui le nom « searchBar ».
Cliquez sur MainStoryboard.storyboard
dans le volet de navigation et ajoutez un contrôle Search Bar and Search Display Controller
à la vue Master
, comme indiqué à la figure suivante.

Cliquez sur Show the Assistant editor
pour afficher côte à côte le canevas et le code MasterViewController.h
, définissez un outlet pour le contrôle Table View
et donnez-lui le nom « laListe ».
Définissez un autre outlet pour le contrôle Search Bar
et donnez-lui le nom « recherche ».
Lors du développement de l'application, vous aurez besoin de trois variables d'instance de type NSMutableArray
. Une pour mémoriser la liste des villes dans sa forme originale et deux autres pour manipuler la liste filtrée. Ajoutez les instructions suivantes dans l'interface de l'application :
NSMutableArray *maListe;
NSMutableArray *tampon;
NSMutableArray *tampon2;
Le fichier MasterViewController.h
doit maintenant ressembler à ceci :
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
NSMutableArray *maListe;
NSMutableArray *tampon;
NSMutableArray *tampon2;
}
@property (weak, nonatomic) IBOutlet UITableViewCell *laListe;
@property (weak, nonatomic) IBOutlet UISearchBar *recherche;
@end
Pour faire fonctionner cette application, deux étapes sont nécessaires.
Ajout de données textuelles dans le
Table View
et sauvegarde dans un objetNSMutableArray
.Filtrage des données affichées dans le
Table View
lorsque des informations sont entrées dans le contrôleSearch Bar
.
Initialisation du Table View
D'une façon traditionnelle, l'initialisation du Table View
se fera dans la méthode viewDidLoad
du contrôleur de vue principal. Cliquez sur MasterViewController.m
dans le volet de navigation et complétez la méthode viewDidLoad
comme suit :
- (void)viewDidLoad
{
[super viewDidLoad];
maListe = [[NSMutableArray alloc] init];
tampon = [[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"];
[tampon addObjectsFromArray:maListe]; //Mémorisation des données d'origine
}
Les deux premières instructions ajoutées dans cette méthode réservent de l'espace en mémoire pour les objets NSMutableArray maListe
et tampon
:
maListe = [[NSMutableArray alloc] init];
tampon = [[NSMutableArray alloc] init];
Les seize instructions suivantes ajoutent des données dans l'objet maListe :
[maListe addObject:@"Paris"];
...
[maListe addObject:@"Brest"];
Enfin, la dernière instruction recopie le contenu de l'objet maListe
dans l'objet tampon
. Comme son nom le laisse supposer, l'objet tampon
sera utilisé comme sauvegarde de l'objet maListe
. Nous verrons prochainement pourquoi cette sauvegarde est importante.
Si vous recherchez dans votre mémoire, vous vous rappellerez certainement la technique permettant de copier les données d'un NSMutableArray
dans un contrôle Table View
. Vous devez définir les méthodes tableView: tableView numberOfRowsInSection: section
et tableView: tableView cellForRowAtIndexPath: indexPath
. La première définit le nombre d'éléments à afficher et la seconde définit chacun des éléments à afficher.
Insérez le code suivant dans le fichier MasterViewController.m
:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [maListe count];
}
- (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];
}
// Configure the cell.
NSString *cellValue = [maListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
Si vous essayez d'exécuter l'application, vous obtiendrez une erreur lors de la compilation. Il reste en effet plusieurs petits détails à régler avant que l'affichage des données dans le Table View
soit opérationnel.
Comme à la figure suivante, cliquez sur MainStoryboard.storyboard
dans le volet de navigation (1), affichez le volet des utilitaires en cliquant sur l'icône Hide or show the Utilities
(2), affichez l'inspecteur des attributs en cliquant sur l'icône Show the Attributes inspector
(3), cliquez sur le Table View
dans le canevas (4) et choisissez Dynamic Prototypes
dans la liste déroulante Content
(5).

Le contenu des cellules est en effet défini dans le code, ce qui correspond au modèle Dynamic Prototypes
.
Cette correction a produit un avertissement matérialisé par un triangle « attention » de couleur jaune dans la partie supérieure de la fenêtre de Xcode (1). Cliquez dessus pour prendre connaissance du problème, puis cliquez sur l'énoncé du problème dans la partie gauche de la fenêtre (2). Il ne vous reste plus qu'à donner un identifiant au prototype pour régler ce problème (3), comme indiqué à la figure suivante.

Juste histoire de souffler un peu, vous pouvez exécuter l'application en cliquant sur Run
et constater que le Table View
contient des données.
Filtrage des données affichées dans le Table View
Lorsque le contenu du contrôle Search Bar
change, la méthode searchBar: searchBar textDidChange: searchText
est exécutée. Nous allons donc utiliser cette méthode pour filtrer les données affichées dans le Table View
.
Mais avant de plonger dans le code, je vous rappelle que :
les données affichées dans le
Table View
proviennent duNSMutableArray maListe
;le
NSMutableArray maListe
a été sauvegardé dans l'objettampon
à la fin de la méthodeviewDidLoad
.
Ajoutez le code suivant dans le fichier MasterViewController.m
:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
tampon2 = [[NSMutableArray alloc] init];
[tampon2 addObjectsFromArray:tampon];
[maListe removeAllObjects];
if([searchText isEqualToString:@""])
{
[maListe removeAllObjects];
[maListe addObjectsFromArray:tampon]; // Restitution des données originales
return;
}
for(NSString *name in tampon2)
{
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
if(r.location== 0)
[maListe addObject:name];
}
}
}
Ne vous laissez pas impressionner par la longueur de cette méthode ! Elle ne contient qu'un peu de logique élémentaire que nous allons facilement décortiquer.
La ligne 3 instancie l'objet tampon2
. En d'autres termes, elle réserve la mémoire pour cet objet :
tampon2 = [[NSMutableArray alloc] init];
La ligne 4 recopie le contenu de l'objet tampon
dans l'objet tampon2
:
[tampon2 addObjectsFromArray:tampon];
La ligne 5 supprime le contenu de l'objet maListe
:
[maListe removeAllObjects];
Les instructions contenues dans le if
suivant (lignes 6 à 11) sont exécutées lorsque la zone de recherche devient vide. Ce cas se produit lorsque l'utilisateur efface le texte qu'il a précédemment tapé.
Lorsque la zone de recherche ne contient plus aucun texte, la liste originale doit être restaurée dans le Table View
. Pour cela, on commence par effacer son contenu :
[maListe removeAllObjects];
Puis l'objet tampon
(qui contient les données originales) est ensuite copié dans l'objet maListe
:
[maListe addObjectsFromArray:tampon];
Il n'est pas nécessaire d'exécuter les instructions suivantes. C'est pourquoi une instruction return
met fin à la méthode.
L'instruction for
de la ligne 13 passe en revue toutes les données contenues dans l'objet tampon2
:
for(NSString *name in tampon2)
La ligne 15 définit l'objet NSRange r
et l'initialise avec la première occurrence du texte tapé dans la zone de recherche dans l'objet tampon2
:
NSRange r = [name rangeOfString:searchText];
Si le texte tapé dans la zone de recherche fait partie de l'élément examiné dans tampon2
:
if(r.location != NSNotFound)
Et si ce texte se trouve au début de l'élément examiné :
if(r.location== 0)
L'élément trouvé est ajouté au NSMutableArray maListe
:
[maListe addObject:name];
La boucle for
examine tour à tour tous les éléments de l'objet maListe
. En fin de boucle, tous les éléments qui commencent par le texte tapé dans la zone de recherche sont donc copiés dans l'objet maListe
.
Après autant de lignes de code, vous brûlez certainement d'envie de lancer l'application. Cliquez sur Run
et profitez de votre application. Vous l'avez bien mérité.
Les fichiers de l'application se trouvent dans le dossier searchBar
.
MasterViewController.h
#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *maListe;
NSMutableArray *tampon;
NSMutableArray *tampon2;
}
@property (weak, nonatomic) IBOutlet UITableViewCell *laListe;
@property (weak, nonatomic) IBOutlet UISearchBar *recherche;
@end
MasterViewController.m
#import "MasterViewController.h"
@implementation MasterViewController
@synthesize laListe;
@synthesize recherche;
- (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];
maListe = [[NSMutableArray alloc] init];
tampon = [[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"];
[tampon addObjectsFromArray:maListe]; //Mémorisation des données d'origine
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [maListe count];
}
- (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];
}
// Configure the cell.
NSString *cellValue = [maListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
tampon2 = [[NSMutableArray alloc] init];
[tampon2 addObjectsFromArray:tampon];
[maListe removeAllObjects];
if([searchText isEqualToString:@""])
{
[maListe removeAllObjects];
[maListe addObjectsFromArray:tampon]; // Restitution des données originales
return;
}
for(NSString *name in tampon2)
{
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
if(r.location== 0)
[maListe addObject:name];
}
}
}
- (void)viewDidUnload
{
[self setLaListe:nil];
[self setRecherche: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
Gestion simultanée de deux vues sur un iPad
Le modèle Master-Detail Application
est particulièrement bien adapté aux iPad. Leur surface d'affichage, bien plus grande que celle offerte par les iPhone et iPod Touch, permet aux applications basées sur ce modèle de rassembler deux vues : une liste et une vue détaillée de l'élément sélectionné dans la liste. L'application iPad Mail (figure suivante) est un parfait exemple d'utilisation de ce modèle.

Et maintenant, passons sans plus attendre à la pratique.
Définissez une nouvelle application basée sur le modèle Master-Detail Application
, donnez-lui le nom « masterDetail » et choisissez iPad
dans la liste déroulante Device Family
.
Cliquez sur MainStoryboard.storyboard
dans le volet de navigation et observez les nombreux objets qui ont été créés, visibles à la figure suivante.

Comme vous pouvez le voir, l'application contient :
un contrôleur de vue
Split View
;un contrôleur de navigation et un
Table View
liés auMaster View Controller
;un contrôleur de navigation et un contrôleur de vue détaillée.
Exécutez l'application en cliquant sur l'icône Run
. La figure suivante représente le résultat obtenu en mode portrait et en mode paysage. Pas si mal pour un début !


La vue Master
consiste en un contrôle Table View
. La vue Detail
représente les détails de l'élément sélectionné dans la vue master
.
Je sens que vous avez envie de vous dégourdir les doigts. Ça tombe bien, nous allons personnaliser l'application qui vient d'être générée par Xcode pour qu'elle affiche trois éléments dans la liste et qu'un clic sur l'un d'entre eux provoque l'affichage d'une image et d'un texte dans la vue détaillée. À la figure suivante se trouve le résultat à obtenir.

Pour arriver à ce résultat, voici les étapes à accomplir :
suppression du texte affiché dans la vue détaillée au lancement de l'application ;
modification du texte affiché dans la barre d'outils ;
définition des entrées textuelles du contrôle
Table View
;ajout de trois images dans les ressources de l'application et d'un contrôle
Image View
dans la vue détaillée ;dans la vue détaillée, affichage de l'image et du texte correspondant à l'entrée cliquée dans le contrôle
Table View
.
Commençons par supprimer le texte affiché dans la vue détaillée au lancement de l'application. Cette étape est très simple : cliquez sur l'entrée MainStoryboard.storyboard
dans le volet de navigation. Repérez le Label
dans la vue Detail
, cliquez dessus puis appuyez sur la touche Suppr du clavier pour le supprimer.
Nous allons maintenant modifier le texte affiché dans la barre d'outils. Cliquez sur DetailViewController.m
dans le volet de navigation. Repérez la méthode suivante :
splitViewController:willHideViewController:withBarButtonItem:forPopoverController:
… et modifiez sa première instruction comme suit :
barButtonItem.title = @"À vous de choisir";
Nous en sommes déjà à la troisième étape, à savoir, la définition des entrées affichées dans le contrôle Table View
. Nous allons pour cela utiliser la méthode viewDidLoad
du fichier MasterViewController.m
. Cliquez sur ce fichier dans le volet de navigation et complétez la méthode viewDidLoad
comme suit :
- (void)viewDidLoad
{
[super viewDidLoad];
laListe = [[NSMutableArray alloc] init];
[laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
self.navigationItem.title = @"Choisissez un animal";
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
La première instruction initialise le NSMutableArray laListe
:
laListe = [[NSMutableArray alloc] init];
Les trois instructions suivantes ajoutent trois entrées dans le tableau laListe
:
[laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
Enfin, la dernière instruction ajoutée définit le titre qui sera affiché dans la fenêtre popup :
self.navigationItem.title = @"Choisissez un animal";
L'objet laListe
a été initialisé dans la méthode viewDidLoad
, mais il n'a pas été défini. Pour réparer cette lacune, cliquez sur MasterViewController.h
dans le volet de navigation, puis ajoutez les lignes 2 à 4 sous la définition de l'interface :
@interface MasterViewController : UITableViewController
{
NSMutableArray *laListe;
}
Pour afficher les données dans le Table View
, vous devez encore insérer deux méthodes dans le fichier MasterViewController.m
:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [laListe count];
}
- (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 = [laListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
Attention, si vous essayez d'exécuter l'application, vous obtiendrez une erreur lors de la compilation. Il reste en effet plusieurs petits détails à régler avant que l'affichage des données dans le Table View
soit opérationnel :
le contenu des cellules étant défini dans le code, il faut utiliser un Dynamic Prototypes ;
un identifiant doit être affecté au prototype.
Vous pouvez dès maintenant exécuter l'application et vérifier que le Table View
affiche bien les trois cellules qui ont été renseignées.
Maintenant, ajoutez trois images dans les ressources de l'application. Si vous voulez utiliser les mêmes images que moi, vous pouvez les télécharger ici.
Ensuite, cliquez sur l'entrée MainStoryboard.storyboard
dans le volet de navigation. Repérez la vue Detail
et ajoutez-y un contrôle Image View
et un contrôle Label
. Redimensionnez et repositionnez ces contrôles afin d'obtenir quelque chose comme la figure suivante.

Définissez l'outlet uneImage
pour le contrôle Image View
et l'outlet laLegende
pour le contrôle Label
. Ces opérations doivent maintenant vous être familières. Au besoin, reportez-vous aux chapitres précédents pour avoir plus de détails sur la technique à utiliser.
L'application est presque terminée : il ne reste plus que la cinquième étape, l'affichage de l'image et du texte qui correspondent à l'élément choisi par l'utilisateur dans le Table View
.
Avant tout, vous devez déterminer quel élément a été choisi par l'utilisateur en définissant la méthode didSelectRowAtIndexPath
dans la vue Master
. Cliquez sur MasterViewController.m
et définissez la méthode didSelectRowAtIndexPath
comme suit :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailViewController.detailItem = [NSString stringWithFormat: @"%@", [laListe objectAtIndex: indexPath.row]];
}
L'unique instruction de cette méthode récupère l'élément choisi, le convertit en un NSString
et le stocke dans la variable detailItem
de la vue détaillée.
Il ne reste plus qu'à récupérer la donnée passée dans la vue détaillée et à afficher l'image et le texte correspondants. Cela se fera dans la méthode configureView
de la vue Detail
.
Cliquez sur DetailViewController.m
dans le volet de navigation. Repérez la méthode configureView
et complétez-la comme suit :
- (void)configureView
{
self.laLegende.text = [self.detailItem description];
self.uneImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]];
}
Cette méthode est également très courte.
La ligne 3 récupère la chaîne stockée dans l'objet detailItem
. Cette chaîne est affectée à la propriété texte du Label
laLegende
, ce qui provoque son affichage dans la vue détaillée.
La ligne 4 vous semble peut-être un peu compliquée. Cela vient de l'imbrication de trois messages dans la deuxième partie de l'instruction. N'ayez crainte, il n'y a rien d'insurmontable dans cette instruction. Pour bien comprendre ce qui se passe, il faut toujours partir du message le plus interne. Ici :
[self.detailItem description]
Ce message a déjà été utilisé dans l'instruction précédente. Il renvoie la version texte du contenu de l'objet detailItem
. Jusque-là, tout va bien !
Passons au message qui engloble [self.detailItem description]
:
[NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]
Ce message crée un objet NSString
en concaténant la valeur sélectionnée par l'utilisateur et la chaîne « .jpg ».
Supposons que l'utilisateur choisisse « chat » dans le Table View
. Ce message produira un NSString
contenant la chaîne « chat.jpg ».
Passons enfin au troisième message :
[UIImage imageNamed: ...]
Ce message définit un objet UIImage
en piochant dans les ressources de l'application. Le fichier sélectionné est précisément celui dont le nom a été calculé à l'étape précédente.
Pour résumer, ces trois messages imbriqués fabriquent un objet UIImage
dont le nom est égal au contenu de la cellule sélectionnée par l'utilisateur, auquel on ajoute « .jpg ». Vous voyez, tout cela est très simple
Ah oui, j'allais oublier l'essentiel : une fois l'objet UIImage
obtenu, il est affecté à la propriété image
du contrôle Image View
(self.uneImage.image = …
). Ainsi, l'image est affichée dans la vue Detail
.
Il ne vous reste plus qu'à cliquer sur Run
et à profiter du résultat (figure suivante).

Les fichiers de l'application se trouvent dans le dossier masterDetail
.
MasterViewController.h
#import <UIKit/UIKit.h>
@class DetailViewController;
@interface MasterViewController : UITableViewController
{
NSMutableArray *laListe;
}
@property (strong, nonatomic) DetailViewController *detailViewController;
@end
MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
@implementation MasterViewController
@synthesize detailViewController = _detailViewController;
- (void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[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];
laListe = [[NSMutableArray alloc] init];
[laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
self.navigationItem.title = @"Choisissez un animal";
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [laListe count];
}
- (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 = [laListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (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 YES;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailViewController.detailItem = [NSString stringWithFormat: @"%@", [laListe objectAtIndex: indexPath.row]];
}
@end
DetailViewController.h
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController <UISplitViewControllerDelegate>
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIImageView *uneImage;
@property (weak, nonatomic) IBOutlet UILabel *laLegende;
@end
DetailViewController.m
#import "DetailViewController.h"
@interface DetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
@implementation DetailViewController
@synthesize detailItem = _detailItem;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;
@synthesize uneImage = _uneImage;
@synthesize laLegende = _laLegende;
@synthesize masterPopoverController = _masterPopoverController;
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
self.laLegende.text = [self.detailItem description];
self.uneImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]];
}
- (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];
}
- (void)viewDidUnload
{
[self setUneImage:nil];
[self setLaLegende: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 YES;
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = @"A vous de choisir";
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
@end
En résumé
Les contrôles
Tab Bar
permettent de créer facilement des applications multivues. L'utilisateur passe d'une vue à l'autre en cliquant sur les icônes du contrôleTab Bar
, rattachés à la classeUITabBar
.Le modèle
Master-Detail Application
est particulièrement bien adapté aux iPad. Leur surface d'affichage, bien plus grande que celle des iPhone et iPod Touch, permet aux applications basées sur ce modèle de rassembler deux vues : une liste et une vue détaillée de l'élément sélectionné dans la liste.Vous utiliserez un contrôle
Tool Bar
chaque fois qu'il est nécessaire d'ajouter une barre d'outils dans une application. Les contrôlesTool Bar
relèvent de la classeUIToolBar
. Ils peuvent être composés d'un ou de plusieurs boutons rattachés à la classeUIBarButtonItem
. Ces boutons peuvent contenir du texte et/ou une image.Le contrôle
Search Bar
est très pratique lorsqu'il est nécessaire d'effectuer une recherche dans une application. Ce contrôle relève de la classeUISearchBar
.