• 20 heures
  • {0} Facile|{1} Moyenne|{2} Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

J'ai tout compris !

Mis à jour le 21/06/2013

Barres et tabulations

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

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.

Image utilisateur

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 permettent de changer de vue simplement

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.

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

L'application est opérationnelle, il est possible de changer de vue

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

Insérez une troisième vue et reliez-la au Tab Bar Controller

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

L'application comporte trois vues

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.

Les icônes que j'ai utilisées dans l'application

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

Il faut utiliser l'inspecteur des attributs pour modifier l'apparence d'un Tab Bar Item

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 :

Image utilisateur

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.

Image utilisateur

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.

  1. Glissez-déposez un View Controller de la bibliothèque d'objets sur le canevas (1).

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

  3. Relâchez la touche Contrôle et le pointeur de la souris et sélectionnez Relationship - viewControllers dans le menu (3).

Image utilisateur

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 :

Image utilisateur

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 :

Image utilisateur

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.

Télécharger les icônes

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

Image utilisateur

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.

L'application doit ressembler à ça

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.

Positionnez les éléments de cette façon

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.

L'application doit ressembler à ça

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.

Ajoutez un contrôle Search Bar and Search Display Controller à la vue Master

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.

  1. Ajout de données textuelles dans le Table View et sauvegarde dans un objet NSMutableArray.

  2. Filtrage des données affichées dans le Table View lorsque des informations sont entrées dans le contrôle Search 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).

Choisissez Dynamic Prototypes dans la liste déroulante Content

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.

Donnez un identifiant au prototype pour régler le problème

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 du NSMutableArray maListe ;

  • le NSMutableArray maListe a été sauvegardé dans l'objet tampon à la fin de la méthode viewDidLoad.

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.

L'application Mail sur iPad

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.

De nombreux objets ont été créés

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 au Master 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 !

Image utilisateur
Image utilisateur

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.

Notre application ressemblera à ça

Pour arriver à ce résultat, voici les étapes à accomplir :

  1. suppression du texte affiché dans la vue détaillée au lancement de l'application ;

  2. modification du texte affiché dans la barre d'outils ;

  3. définition des entrées textuelles du contrôle Table View ;

  4. ajout de trois images dans les ressources de l'application et d'un contrôle Image View dans la vue détaillée ;

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

Placez les contrôles comme ceci

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

L'application est terminée et fonctionne

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ôle Tab Bar, rattachés à la classe UITabBar.

  • 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ôles Tool Bar 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.

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

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