8

Assembler les écrans de l’application

Nous avons vu dans le chapitre précédent comment implémenter un écran de l’application. Cependant, les applications sont en très grande majorité construites à l’aide de plusieurs écrans qui s’enchaînent selon un des design patterns de navigation décrits au chapitre 5.

Dans ce chapitre, nous étudierons deux méthodes pour assembler les écrans de l’application : la première consiste à utiliser des contrôleurs conçus pour contenir vos contrôleurs de vue et la seconde se sert de méthodes de UIViewController pour afficher des contrôleurs de vue modale.

Généralités sur les contrôleurs-conteneurs

Les deux contrôleurs-conteneurs proposés sont le contrôleur de navigation et celui d’onglets. On trouve également le UISplitViewController particulier à l’iPad. Ils sont fournis par UIKit et diffèrent des contrôleurs de vue standards sur trois points très importants.

Ils contiennent d’autres contrôleurs et leur transmettent les événements.

Ils ne sont pas soumis à héritage dans votre application. Vous devez les utiliser tels qu’ils sont fournis et ne pas chercher à surcharger leurs méthodes.

Ils gèrent plusieurs écrans de l’application (en s’appuyant sur vos propres contrôleurs).

CONSEIL Évitez de créer d’autres contrôleurs-conteneurs

Bien qu’il soit possible de créer vos propres contrôleurs-conteneurs, ce processus est relativement complexe. Nous n’aborderons donc pas ce sujet.

Le contrôleur de navigation

La classe UINavigationController fournit toute la logique nécessaire pour implémenter le design pattern de navigation dans des listes hiérarchiques. Elle prend en charge l’affichage de la barre de navigation en haut de l’écran (titre et boutons), les animations entre les écrans et de nombreux petits détails indispensables pour retrouver dans votre application la même fluidité que dans les applications de l’iPhone et de l’iPad.

image

Figure 8–1 Le contrôleur de navigation permet d’assembler les différents contrôleurs utilisés dans vos listes hiérarchiques.

Lorsque vos contrôleurs de vue sont ajoutés dans un contrôleur de navigation, ils sont automatiquement redimensionnés pour s’ajuster à la taille disponible sur l’écran (le contrôleur de navigation prend en compte la taille de la barre de navigation en haut, mais aussi celles de la barre d’onglets et de la barre de boutons qui peuvent également être présentes).

Création d’un contrôleur de navigation

Il suffit d’instancier la classe UINavigationController en lui fournissant un contrôleur qui sera le premier écran de la hiérarchie (et donc affiché immédiatement).

UINavigationController *myNavController =

[[UINavigationController alloc]

initWithRootViewController:myViewController];

image

Figure 8–2 Ajout d’un contrôleur de navigation comme conteneur d’un contrôleur

Spécifier le contenu de la barre de navigation

La barre de navigation est divisée horizontalement en trois parties :

une zone à gauche dans laquelle le bouton Retour vient se placer lorsqu’il existe une vue précédente ;

une zone centrale pour afficher le titre de la vue en cours ;

une zone à droite qui peut servir à afficher un bouton d’action (comme le bouton + dans l’application Contact, par exemple).

image

Figure 8–3 Les différentes zones de la barre de navigation

Tous ces éléments sont paramétrés par l’intermédiaire d’une propriété qui est présente sur tous les contrôleurs de vue, mais qui n’est utilisée que lorsque le contrôleur est ajouté à un contrôleur de navigation. Il s’agit de la propriété navigationItem qui est un objet du type UINavigationItem.

Le point important à retenir ici est que chaque contrôleur de vue indique, via cette propriété, comment il souhaite être représenté. Le contrôleur de navigation va lire ces informations et anime correctement les transitions pour s’assurer qu’elles aient toujours l’air naturel.

Titre du contrôleur

La propriété la plus importante de cet objet est title qui indique le titre du contrôleur. On peut par exemple la définir dans le constructeur du contrôleur de vue.

Définition du titre d’un contrôleur de vue

- (id) initWithNibName:(NSString *)nibNameOrNil

 bundle:(NSBundle *)nibBundleOrNil

{

if (self = [super initWithNibName:nibNameOrNil

  bundle:nibBundleOrNil])

{

self.navigationItem.title = @"Ma vue";

}

return self;

}

Pour afficher autre chose que du texte dans la zone de titre, il est possible de fournir une vue via la propriété titleView. Dans ce cas, la propriété title n’est plus utilisée. Nous étudierons les vues en détail dans le prochain chapitre, mais l’exemple ci-après montre comment utiliser une image comme titre.

Utilisation d’une image comme titre d’un contrôleur dans une liste de navigation

UIImageView *myTitleView =

[[UIImageView alloc]

initWithImage:[UIImage imageNamed:@"mytitle.png"]];

self.navigationItem.titleView = myTitleView;

Enfin, il est également possible de définir un prompt (propriété prompt) qui est un message affiché au-dessus du titre, pour indiquer à l’utilisateur ce qu’il doit faire dans cet écran.

image

Figure 8–4 Une barre de navigation avec une invite

Boutons supplémentaires

Par défaut, et si le contrôleur en cours n’est pas le premier, le contrôleur de navigation ajoute un bouton de retour à l’écran précédent. Sinon, il n’affiche rien d’autre que le titre.

Il est possible de remplacer ce bouton et même d’en afficher un supplémentaire dans la partie droite de la barre. Pour cela, on renseigne les deux propriétés leftBarButtonItem et rightBarButtonItem de navigationItem. Tous les deux sont des objets UIBarButtonItem. Il est possible d’instancier UIBarButtonItem à partir d’une image, d’un texte ou bien d’une icône système.

Mise en place d’un bouton Appareil photo dans la barre de navigation

UIBarButtonItem *item =

[[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemCamera

 target:self

 action:@selector(buttonCameraPressed)];

self.navigationItem.rightBarButtonItem = item;

BONNE PRATIQUE Ne changez pas le bouton de gauche

À moins que votre contrôleur soit le premier de l’application, il n’est pas du tout recommandé de mettre un autre bouton à la place qu’occupe habituellement le bouton de retour vers l’appelant. En effet, vous devriez alors proposer un autre mécanisme pour permettre à l’utilisateur de revenir en arrière dans son parcours de navigation et le résultat serait déroutant.

Définir la façon dont est représenté le contrôleur quand il n’est plus affiché

Lorsque l’utilisateur avance d’un niveau dans la navigation et qu’un autre contrôleur vient recouvrir la vue actuelle, votre contrôleur est encore représenté sous la forme d’une flèche dans la partie gauche de la barre de navigation.

Par défaut, cette flèche contient le titre de votre contrôleur (il sera tronqué automatiquement s’il est trop long), mais vous pouvez fournir votre propre UIBarButtonItem si vous le souhaitez, via la propriété backBarButtonItem.

CONSEIL Aider l’utilisateur à retrouver son chemin

En indiquant juste « Retour » sur le bouton permettant de revenir à votre contrôleur, vous n’aidez pas l’utilisateur à se souvenir de ce qu’il trouvera dans cet écran.

Il est recommandé d’utiliser un nom court et clair qui informe l’utilisateur sur ce qu’il y a derrière ce bouton. En observant les applications iOS, vous verrez que ce petit détail est très souvent mis en application : dans les courriels par exemple, le nom de la boîte e-mail ou du dossier est indiqué. Il correspond fréquemment à l’intitulé qui l’a déclenché.

Masquer la barre d’outils ou la barre d’onglets

Comme nous le verrons dans la suite de ce chapitre, il est possible d’utiliser un contrôleur de navigation en combinaison avec des barres d’outils ou avec une barre d’onglets.

Dans certaines applications, on peut vouloir masquer cette barre pour certains des écrans. C’est souvent le cas quand on affiche des photos, par exemple.

La propriété hidesBottomBarWhenPushed du contrôleur de navigation indique que la barre située en bas de l’écran doit être masquée lorsque ce contrôleur est affiché.

self.hidesBottomBarWhenPushed = YES;

Pousser des écrans dans le contrôleur de navigation

Lorsque l’utilisateur sélectionne un élément dans la vue, le contrôleur courant peut demander au contrôleur de navigation d’animer la transition vers le prochain contrôleur (le prochain écran vers la droite).

On appelle pour cela la méthode pushViewController:animated: qui ajoute un contrôleur à un niveau inférieur dans la hiérarchie et anime une transition.

APPROFONDIR Quand utiliser le paramètre animated

De nombreuses méthodes du SDK prennent un paramètre animated qui indique s’il faut animer l’apparition d’un élément. La plupart du temps, on souhaite afficher une animation, mais il est parfois souhaitable d’amener l’utilisateur directement à un écran (lorsqu’on relance l’application, par exemple).

Le contrôleur de navigation gère la pile des contrôleurs. Il va y ajouter le nouveau contrôleur passé en paramètre, l’afficher et le retenir en mémoire jusqu’à ce qu’il ne soit plus utilisé. Le retour en arrière est le plus souvent provoqué par l’utilisateur lorsqu’il utilise le bouton de gauche dans la barre de navigation.

Le contrôleur de vue qui est contenu dans le contrôleur de navigation a besoin d’une référence vers son contrôleur-conteneur pour pouvoir appeler la méthode pushViewController:.

Il la trouvera toujours dans sa propriété navigationController : en effet, à chaque fois qu’un contrôleur est ajouté dans un contrôleur de navigation, la propriété navigationController est définie automatiquement.

Création d’un nouveau contrôleur de vue et ajout dans le contrôleur de navigation

- (IBAction) buttonAction {

MyViewController *myViewController =

[[MyViewController alloc] initWithNibName:nil bundle:nil];

[self.navigationController pushViewController:myViewController animated:YES];

}

Avec un storyboard, on utilise une segue en sélectionnant l’action push pour charger le contrôleur suivant. On retrouvera un peu plus loin un exemple de code illustrant ce mécanisme.

Personnaliser la barre de navigation

La barre de navigation elle-même peut être personnalisée. Via la propriété navigationBar de l’objet navigationController, vous accédez à l’objet UINavigationBar qui est une vue représentant la barre.

Les propriétés les plus intéressantes sont décrites dans le tableau ci-après.

Propriété Utilisation
Style de la barre (barStyle) Détermine le style de la barre : bleu (UIBarStyleDefault) ou noir (UIBarStyleBlack).
Transparence de la barre (translucent) Indiquer que la barre doit être transparente. Dans ce cas, le contrôleur de navigation fera automatiquement en sorte que les vues de vos contrôleurs de vues passent en dessous de la barre.
Couleur de la teinte de la barre (tintColor) Cette propriété fournit une couleur qui sera utilisée comme teinte de la barre.

Exemple complet de navigation avec un storyboard

Créer un nouveau projet basé sur de la navigation est extrêmement simple et la structure de base ne nécessite aucune ligne de code, il faut même en enlever…

1 Créer un nouveau projet de type Empty Based (prendre un projet de type Universal sans storyboard). Le nommer NavProjetSB.

2 Créer une nouvelle classe de type UIViewController appelée ViewController1 sans XIB et une seconde appelée ViewController2 sans XIB non plus.
Le projet ainsi créé présente alors l’aspect suivant.

image

Figure 8–5 Structure du projet de navigation, une fois les éléments créés

3 Ajouter un storyboard, en choisissant New File>User interface>Storyboard. Sélectionner l’option iPhone et appeler le fichier MainStoryboard_iPhone. Supprimer le code se trouvant dans l’AppDelegate.m. Il ne doit rester que les lignes suivantes :

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

return YES;

}

// Le reste du code est inchangé.

4 Associer le storyboard au projet en cliquant sur ce dernier et en associant le nom du storyboard à l’option intitulée Main Storyboard. En cas d’utilisation d’un projet iPad, penser à faire un second storyboard (que vous pourrez appeler MainStoryboard_iPad) et à l’associer à l’option Main Storyboard file base name (iPad) de la section Info du projet.

5 Cliquer sur le fichier storyboard et amener un contrôleur de navigation de la bibliothèque d’outils graphiques du panneau Utilities en bas à droite.

Le nouveau Contrôleur de navigation arrive par défaut avec un TableView Controller. Il faut le supprimer, mais garder la partie gauche qui pose la logique de navigation.

Puis, ajouter un nouveau ViewController simple et l’associer en tirant une connexion (segue) entre la partie gauche et la partie droite. Un petit menu apparaît, choisir rootViewController comme type de relation.

On obtient l’aspect suivant dans le storyboard :

image

Figure 8–6 Un projet de type Navigation.

6 Ajouter un second ViewController dans le storyboard, qui se placera à droite des structures existantes.

7 Dans le dock du premier contrôleur, choisir ViewController1 dans l’inspecteur d’identité.

8 Dans le dock du second contrôleur, choisir ViewController2 dans l’inspecteur d’identité.

9 Connecter le bouton au second contrôleur, pour faire apparaître une transition (segue) entre les deux contrôleurs de vue. Choisir un type push.

10 Compiler et tester. On obtient un contrôleur de navigation fait avec très peu de code.

image

Figure 8–7 Une segue dans une structure Navigation

Exemple complet de navigation entièrement codé

Bien qu’il soit conseillé de passer par l’utilisation d’un storyboard, on peut être amené à maintenir du code qui implémente un contrôleur de navigation sans XIB ni storyboard.

Ainsi, un projet qui utiliserait un contrôleur de navigation serait réalisé de la façon suivante.

1 Créer un nouveau projet de type Empty Based (prendre à nouveau un projet de type Universal sans storyboard). Le nommer NavProjet.

2 Créer une nouvelle classe de type UIViewController appelée ViewController1 sans XIB et une seconde appelée ViewController2 sans XIB non plus.

Le projet ainsi créé présente alors l’aspect suivant.

image

Figure 8–8 Structure du projet de navigation, une fois les éléments créés

On peut noter que la structure est la même que pour un projet de type storyboard.

3 Dans le fichier AppDelegate.h, déclarer les propriétés du contrôleur de navigation et du ViewController qu’il va embarquer.

#import <UIKit/UIKit.h>

#import "ViewController1.h" // Le premier ViewController à charger

@interface AppDelegate : UIResponder <UIApplicationDelegate> {

// La barre de navigation

UINavigationController *navigationController;

// Le ViewController dans le contrôleur de navigation

ViewController1 *viewController1;

}

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic)

UINavigationController *navigationController;

@property (strong, nonatomic) ViewController1 *viewController1;

@end

4 Dans le fichier AppDelegate.m, allouer le contrôleur de navigation et le premier contrôleur de vue (ViewController1). La vue associée au contrôleur de navigation est alors chargée dans la hiérarchie des fenêtres.

#import "AppDelegate.h"

@implementation AppDelegate

#pragma mark - Propriétés

@synthesize navigationController;

@synthesize viewController1;

#pragma mark - Méthodes

@synthesize window = _window;

- (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

self.window =

[[UIWindow alloc]

initWithFrame:[[UIScreen mainScreen] bounds]];

// Départ de l’application.

// Allocation du contrôleur 1

viewController1 = [[ViewController1 alloc] init];

// Allocation de la barre de navigation

navigationController =

[[UINavigationController alloc]

initWithRootViewController:viewController1];

// Association de la vue du contrôleur de navigation

// à la vue de la fenêtre

self.window.rootViewController = navigationController;

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

// Le reste du code est inchangé.

5 Dans le fichier ViewController1.h, déclarer le bouton qui sert de déclencheur et son action associée.

#import <UIKit/UIKit.h>

@interface ViewController1 : UIViewController {

UIView *maView; // Vue du contrôleur

UIButton *monBouton; // Bouton de déclenchement

}

@property (nonatomic,strong) UIView *maView;

@property (nonatomic,strong) UIButton *monBouton;

// Actions

- (IBAction) clicPush:(id)sender; // Associé au bouton

@end

6 Dans le fichier ViewController1.m, synthétiser les propriétés et implémenter le code associé à l’action qui pousse le ViewController2 dans la navigation.

#import "ViewController1.h"

// Ne pas oublier d’importer la définition :

#import "ViewController2.h"

@implementation ViewController1

#pragma mark - Propriétés

@synthesize maView;

@synthesize monBouton;

#pragma mark - Méthodes

// Action associée au bouton

- (IBAction) clicPush:(id)sender {

ViewController2 *viewController2 = [[ViewController2 alloc] init];

[self.navigationController pushViewController:viewController2 animated:YES];

}

// Code inchangé

#pragma mark - View lifecycle

- (void)loadView

{

maView = [[UIView alloc] initWithFrame:CGRectMake(0,

0,

320,

480)];

self.view = maView;

monBouton = [UIButton buttonWithType:UIButtonTypeRoundedRect];

monBouton.frame = CGRectMake(20, 80, 200, 30);

[monBouton setTitle:@"Push" forState:UIControlStateNormal];

[monBouton setOpaque:NO];

[monBouton setAlpha:1];

[monBouton addTarget:self

action:@selector(clicPush:)

forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:monBouton];

self.title = @"Page 1";

}

// Le reste du code est inchangé.

7 Dans le ViewController2.h, on déclare une vue et un label.

#import <UIKit/UIKit.h>

@interface ViewController2 : UIViewController {

UIView *maView; // Vue du contrôleur

UILabel *monLabel; // Label d’identification

}

@property (nonatomic,strong) UIView *maView;

@property (nonatomic,strong) UILabel *monLabel;

@end

8 Dans le fichier ViewController2.m, allouer la vue et le label.

#import "ViewController2.h"

@implementation ViewController2

#pragma mark - Propriétés

@synthesize maView;

@synthesize monLabel;

#pragma mark - Méthodes

// Code inchangé

#pragma mark - View lifecycle

- (void)loadView

{

maView = [[UIView alloc] initWithFrame:CGRectMake(0,

0,

320,

480)];

self.view = maView;

monLabel =

[[UILabel alloc] initWithFrame:CGRectMake(20, 20, 200, 40)];

monLabel.text = @"Arrivée en page 2";

[self.view addSubview:monLabel];

self.title = @"Page 2";

}

// Le reste du code est inchangé.

9 Tester et compiler le projet. La barre de navigation prendra en charge toute la structure retour nécessaire.

Contrôleur d’onglets

La navigation par onglets sert à créer des applications dans lesquelles plusieurs modes parallèles sont proposés à l’utilisateur.

Tout comme le contrôleur de navigation, le contrôleur d’onglets est un composant fourni par le SDK qui implémente toute la logique nécessaire à la gestion d’une barre d’onglets et qui n’est pas conçu pour être dérivé.

Ce composant gère la barre d’onglets affichée en bas de l’écran, mais aussi le mécanisme qui permet, lorsqu’il y a trop d’éléments, de faire apparaître un onglet Autre et de proposer la personnalisation de la barre d’onglets (voir le chapitre 5, section « Navigation par onglets » pour un rappel de ce design pattern de navigation).

Création d’un contrôleur d’onglets

Le contrôleur d’onglets est implémenté dans la classe UITabBarController. Son constructeur prend en paramètre une liste de contrôleurs à afficher dans les onglets.

Création et initialisation d’un contrôleur d’onglets

NSArray *controllers =

[[NSArray alloc] initWithObjects:controller1, controller2, nil];

UITabBarController *tabBarController =

[[UITabBarController alloc] initWithControllers:controllers];

Les onglets sont affichés dans l’ordre de la liste passée en paramètre. S’il y a plus de cinq onglets, les quatre premiers sont affichés et un onglet Et aussi est ajouté en cinquième position. Ce dernier donne accès à une liste de tous les onglets déclarés, gérée automatiquement par iOS.

Il est possible de modifier les onglets visibles en les réorganisant. Le UITabBarController prend automatiquement en charge cette réorganisation ; il ne nécessite aucun code supplémentaire.

Personnalisation du titre et de l’icône des onglets

Comme pour le contrôleur de navigation, chaque contrôleur définit dans une propriété comment il doit être représenté dans la barre d’onglets, et le contrôleur d’onglets va lire cette propriété pour afficher un titre et une icône.

La propriété tabBarItem de type UITabBarItem définit deux propriétés : title et image. L’image est un PNG de 30 × 30 pixels (60 × 60 pixels sur un écran rétina) et il doit avoir une couche alpha (qui indique où l’image est transparente).

Lorsque l’onglet n’est pas sélectionné, la barre d’onglets affiche l’icône fournie en la passant en noir et blanc. Pour cela, elle affiche un pixel gris partout où l’image n’est pas transparente. Lorsque l’onglet est sélectionné, elle affiche les mêmes pixels, mais avec un masque dégradé bleu.

Définition de la propriété tabBarItem

self.tabBarItem.image = [UIImage imageNamed:@"icone.png"];

self.tabBarItem.title = @"titre";

Réagir aux événements de la barre d’onglets

La barre d’onglets peut transmettre des événements lorsque l’utilisateur utilise la fonction de personnalisation.

Pour être notifié, l’objet qui construit la barre d’onglets doit fournir un délégué qui implémente le protocole UITabBarControllerDelegate. Comme c’est le plus souvent le délégué d’application qui crée la barre d’onglets, ce sera généralement lui qui servira directement de délégué.

Mise en place d’un délégué de la barre d’onglets

tabBarController.delegate = self;

Suivre les changements de sélection

Grâce au délégué du contrôleur d’onglets, il est possible d’être informé lorsque l’utilisateur change d’onglet, et même d’empêcher ce changement.

La méthode houldSelectViewController: reçoit en paramètre le viewController vers lequel l’utilisateur veut aller, et attend en retour un booléen. Si la méthode renvoie NO, la barre d’onglets ne change pas l’onglet affiché.

La méthode didSelectViewController: passe en paramètre le contrôleur qui vient d’être sélectionné par l’utilisateur. Elle est appelée à chaque fois que l’utilisateur clique sur l’onglet, même s’il était déjà affiché.

Réagir à la personnalisation de la barre

Il existe dans le délégué deux méthodes très intéressantes pour suivre la personnalisation de la barre.

La première tabBarController: willBeginCustomizingViewControllers: informe que l’utilisateur commence à personnaliser les onglets.

La seconde tabBarController:didEndCustomizingViewControllers:changed: fait savoir que l’utilisateur a fini de modifier la barre d’onglets. Elle reçoit en paramètre la nouvelle liste de contrôleurs de vue et un booléen indiquant si la liste a été modifiée ou pas.

C’est généralement cette seconde méthode qui est la plus utilisée, car elle permet de sauver l’état de la barre d’onglets pour pouvoir recharger la barre dans le même état lors du prochain redémarrage de l’application.

BONNE PRATIQUE
Enregistrement de l’état de la barre d’onglets dans les préférences de l’utilisateur

La classe NSUserDefaults qui sera abordée au chapitre 13 est parfaitement adaptée pour stocker les préférences utilisateur. Elle permet de les lire et de les sauver en quelques lignes de code.

Limiter la personnalisation de la barre d’onglets

Il est possible de limiter les possibilités de personnalisation de la barre d’onglets en renseignant la propriété customizableViewControllers du contrôleur d’onglets.

Cette propriété contient une liste de contrôleurs qui peuvent être personnalisés (déplacés). Par défaut, c’est la liste fournie lors de l’initialisation de la barre d’onglets, mais il est possible de fournir une liste contenant moins d’éléments. Les éléments manquants seront alors figés à la place qui leur a été donnée lors de l’initialisation.

En mettant cette propriété à nil, on empêche toute personnalisation de la barre d’onglets (le bouton Modifier n’apparaîtra plus dans l’onglet Autre).

Exemple de projet avec contrôleurs d’onglets et storyboard

Nous venons de voir comment on peut programmer les différents éléments nécessaires à une navigation avec des contrôleurs d’onglets. L’utilisation d’un storyboard rend la création du squelette de l’application extrêmement simple à monter, ce qui n’empêche pas le programmeur de personnaliser ensuite son code.

Pour créer une application avec un contrôleur d’onglets, on procède de la façon suivante.

1 Créer un nouveau projet de type Empty Based (on prendra un projet de type Universal et sans storyboard). Nommez-le TabProjetSB.

2 Créer une nouvelle classe de type UIViewController appelée ViewController1 sans XIB et une seconde appelée ViewController2 sans XIB non plus.
Le projet ainsi créé présente alors l’aspect suivant.

image

Figure 8–9 Structure du projet de contrôleur d’onglets, une fois les éléments créés

3 Ajouter un storyboard, en choisissant New File > User interface > Storyboard. Choisir l’option iPhone. Appeler le fichier MainStoryboard_iPhone. Supprimer le code se trouvant dans l’AppDelegate.m. Il ne doit rester que les lignes suivantes :

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

return YES;

}

// Le reste du code est inchangé.

4 Associer le storyboard au projet en cliquant sur le projet et en associant le nom du storyboard à l’option intitulée Main Storyboard. En cas d’utilisation d’un projet iPad, penser à faire un second storyboard (que vous pourrez appeler MainStoryboard_iPad) et à l’associer à l’option Main Storyboard file base name (iPad) de la section Info du projet.

5 Cliquer sur le fichier storyboard et amener un UITabBarController de la bibliothèque d’outils graphiques du panneau Utilities en bas à droite.

On obtient l’aspect suivant dans le storyboard.

image

Figure 8–10 Un projet à base de contrôleur d’onglets.

6 Dans le dock du premier contrôleur, choisir ViewController1 dans l’inspecteur d’identité.

7 Dans le dock du second contrôleur, choisir ViewController2 dans l’inspecteur d’identité.

Tester, et voici un contrôleur d’onglets fait sans aucune ligne de code.

Ajouter un nouvel onglet est également très simple.

1 Créer une nouvelle classe de type UIViewController (par exemple ViewController3).

2 Ajouter dans le storyboard un nouveau contrôleur de vue et le connecter à la TabBar pour créer une segue (transition).

3 Associer ce nouveau contrôleur de vue avec sa classe (ViewController3) en utilisant le cube dans le dock et l’Inspecteur d’identité.

4 Tester : tout fonctionne immédiatement.

NOTE Ajouter des images

On peut ajouter des images 30 × 30 pixels et les associer avec chaque onglet pour une meilleure lisibilité de l’application.

Combiner les contrôleurs d’onglets avec des contrôleurs de navigation

Il est possible d’utiliser des contrôleurs de navigation comme onglets dans un contrôleur d’onglets, mais l’inverse n’est pas autorisé.

Combinaison d’un contrôleur d’onglets avec un contrôleur de navigation

UINavigationController *myNavController =

[[UINavigationController alloc]

initWithRootController:onglet3ViewController];

NSArray *controllers =

[[NSArray alloc]

initWithObjects:onglet1ViewController,

onglet2ViewController,

myNavController,

nil];

UITabBarController *tabBarController =

[[UITabBarController alloc] initWithControllers:controllers];

Construction d’une combinaison onglets et navigation avec un storyboard

Le projet combinant les contrôleurs d’onglets et de navigation peut simplement être mis en place en commençant par placer un contrôleur d’onglets.

Ensuite, il faut poser un contrôleur de navigation et effectuer une transition de la TabBar du premier contrôleur vers le second.

On peut ainsi créer un projet qui aura l’aspect suivant.

Affichage d’un contrôleur en mode modal

On parle d’un affichage en mode modal quand la vue affichée vient recouvrir l’ensemble de l’écran et qu’il faut fermer cette vue pour revenir à la vue précédente. Ce mécanisme assure que l’utilisateur reviendra forcément à la vue appelante ; c’est par exemple le cas d’une aide qui revient sur la vue à laquelle elle est liée.

image

Figure 8–11 Un projet à base de contrôleur d’onglets embarquant un contrôleur de navigation

La classe UIViewController fournit une méthode qui affiche un autre contrôleur pardessus le contrôleur actuel, en mode modal. La nouvelle vue recouvre tout l’écran, même si le contrôleur initial fait partie d’un contrôleur-conteneur et qu’il faut qu’elle soit annulée pour revenir à l’état précédent. Il est possible d’empiler plusieurs contrôleurs modaux et également de définir une animation qui servira de transition vers la vue modale.

Pousser une nouvelle vue modale

Pour pousser une nouvelle vue modale, le contrôleur doit créer un nouveau contrôleur de vue puis appeler la méthode presentModalViewController: animated:completion:. Le premier paramètre est le contrôleur à afficher et le second indique s’il faut déclencher une animation.

Exemple : déclenchement d’une vue modale lorsqu’un bouton est pressé

- (void) chooseAvatarButtonAction

{

MyAvatarPickerController *chooseAvatarViewController =

[[MyAvatarPickerController alloc] init];

[self presentViewController:chooseAvatarViewController

animated:YES completion:nil];

}

Faire disparaître une vue modale

Pour faire disparaître une vue modale, il faut appeler la méthode dismissModalViewControllerAnimated:completion: sur le contrôleur qui détient la vue modale (c’est-à-dire celui qui l’a affichée en appelant presentModalViewController).

Dans certains cas, c’est la vue modale elle-même qui sait quand elle doit disparaître. Dans ce cas, elle peut retrouver une référence vers le contrôleur qui la détient via sa propriété parentViewController.

Exemple : disparition de la vue modale lorsqu’on clique sur un bouton

- (void) buttonAction

{

[self dismissViewControllerAnimated:YES completion:nil];

}

Définir le mode de transition

C’est le contrôleur modal qui définit quelle est la transition qui sera utilisée pour le faire apparaître et le faire disparaître.

La propriété modalTransitionStyle du contrôleur peut prendre une des valeurs du tableau ci-après.

Tableau 8–1 Types de transitions possibles pour une vue modale
Transition Effet
CoverVertical La vue modale apparaît par le bas de l’écran et monte jusqu’à recouvrir entièrement l’écran. Elle fait le chemin inverse pour disparaître.
FlipHorizontal La vue actuelle pivote sur un axe vertical pour révéler la vue modale (effet de l’application Météo lorsqu’on clique sur le bouton de paramétrage).
CrossDissolve La nouvelle vue apparaît via un effet de fondu enchaîné.
PartialCurl La nouvelle vue commence à apparaître avec un effet de page qui se tourne.

Construction d’une transition modale avec les storyboards

Il s’agit là encore d’un mécanisme très simple puisque le bouton qui sert lors de la transition sera associé à la segue qui lie le bouton au contrôleur suivant. L’une des options est justement modal.

Néanmoins, le développeur sera tout de même chargé de gérer le retour du contrôleur appelé en implémentant la méthode dismissModalViewControllerAnimated.

Construction d’un SplitViewController avec les storyboards

Pour un iPad, le SDK iOS nous propose un élément spécifique appelé SplitViewController. On peut directement le sélectionner à la création d’un projet en choisissant un nouveau projet de type Master-Detail. On peut choisir un projet universel et noter que la création des storyboards est cochée.

Avec cette classe spécifique à l’iPad, mais qui est tout de même utilisable sur un iPhone sans l’ergonomie iPad, on bénéficie d’une liste d’éléments dans une liste placée à gauche et du détail des éléments sélectionnés à droite. Si on travaille avec un iPhone dans ce type de projet, celui-ci présente une liste sur l’ensemble de l’écran et lorsqu’un élément de la liste est sélectionné, on passe sur une vue présentant la partie détaillée, par un système de navigation traditionnel.

Le SplitViewController nécessite des connaissances sur les listes d’éléments qui sont traitées dans le chapitre 11. Nous ne présenterons donc qu’un simple projet.

Pour créer un SplitViewController avec des storyboards, on procède de la façon suivante.

1 Une fois n’est pas coutume, créer un nouveau projet de type Master-Detail. Le nommer SPVProjetSB.

2 On peut noter la présence de la classe MasterViewController qui va gérer la liste des éléments et de la classe DetailViewController qui se chargera du détail d’un élément.

image

Figure 8–12 Structure du projet avec SplitViewController

3 Remplir la Table View.

REMARQUE Fonctionnement des listes d’éléments

Vous retrouverez des explications détaillées sur le fonctionnement des listes d’éléments au chapitre 11. Nous le fournissons déjà pour vous permettre de tester votre code immédiatement.

4 4Dans le fichier MasterViewController.h, ajouter une liste de type NSMutableArray.

#import <UIKit/UIKit.h>

@class DetailViewController;

@interface MasterViewController : UITableViewController {

NSMutableArray *liste;

}

@property (strong,nonatomic) NSMutableArray *liste;

@property (strong, nonatomic)

DetailViewController *detailViewController;

@end

5 Dans le fichier MasterViewController.m, ajouter les méthodes de remplissage suivantes.

#import "MasterViewController.h"

#import "DetailViewController.h"

@implementation MasterViewController

#pragma mark - Propriétés

@synthesize liste;

#pragma mark - Méthodes DataSource du TableView

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {

// Nombre de sections de la TableView.

return 1;

}

- (NSInteger) tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section

{

// Nombre de cellules pour chacune des sections.

return [liste count];

}

// Personnalisation des cellules.

- (UITableViewCell *) tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell =

[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell = [[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault

reuseIdentifier:CellIdentifier];

}

// Configuration de la cellule …

cell.textLabel.text =

[NSString

stringWithFormat:@"%@",

[liste objectAtIndex:indexPath.row]];

[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];

return cell;

}

#pragma mark - Méthodes Delegate du TableView

- (void) tableView:(UITableView *)tableView

didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

NSLog(@"Choix %@ se trouvant à l’index %d",

[liste objectAtIndex:indexPath.row], indexPath.row);

}

#pragma mark - Méthodes Apple

- (void)viewDidLoad

{

[s

Ces méthodes seront revues en détail au chapitre 11.

6 Le résultat obtenu est le suivant.

image

Figure 8–13 Aspect final du SplitViewController

Conclusion

Nous avons vu dans ce chapitre comment assembler les écrans d’une application iOS pour construire un ensemble cohérent qui reprenne les standards de conception iOS.

Les méthodes présentées devraient répondre à la très grande majorité des besoins. Si vous ressentez l’envie de faire vos propres contrôleurs-conteneurs, commencez par revérifier que votre objectif ne peut pas être atteint avec les éléments fournis en standard par Apple et envisagez de revoir la navigation de votre application : si elle n’est pas réalisable facilement, il y a de fortes chances qu’elle ne respecte pas les standards d’ergonomie et vous risquez donc un rejet de la part d’Apple.

Propriété de Marie Richie <FB1245387-C111754-prod@customers.feedbooks.com>