• 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

Les contrôles d'action

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

Comme leur nom le laisse supposer, les contrôles d'action sont utilisés pour effectuer des actions lorsque l'utilisateur interagit avec l'écran de son device.

Dans cette section, vous allez apprendre à relier un contrôle d'action (bouton de commande, slider, zone de texte modifiable, etc.) au code de l'application afin de gérer un événement utilisateur. Si nécessaire, vous pourrez même créer plusieurs liaisons pour gérer plusieurs types d'actions. La technique est la même, seul le code à exécuter change.

C'est (entre autres) en choisissant soigneusement les contrôles d'action utilisés qu’une application sera intuitive, agréable et facile à utiliser. Tournez vites les pages et découvrez les contrôles d’action utilisables sur un device iOS 5.

Boutons

Les boutons sont les contrôles d'action de base. Leur apparence peut être personnalisée en utilisant Interface Builder ou des instructions Objective-C. Vous pouvez par exemple choisir la forme, le texte, l'image, la couleur d'arrière-plan et encore beaucoup d'autres caractéristiques.
Interface Builder donne accès à cinq types de boutons prédéfinis, comme le montre la figure suivante.

Les cinq boutons prédéfinis par Interface Builder

Pour transformer un bouton par défaut (Rounded Rect) en un bouton prédéfini, il suffit de faire votre choix dans la liste déroulante Type, comme indiqué à la figure suivante.

Vous pouvez faire votre choix dans la liste déroulante Type

Il est également possible d'ajouter une image à un bouton Rounded Rect : insérez une image de petite taille dans les ressources de l'application et sélectionnez-la dans la propriété Image du bouton. Si nécessaire, complétez cette image avec un texte, comme à la figure suivante.

Il est possible d'ajouter une image en plus du texte à un bouton

Enfin, il est également possible de créer un bouton en utilisant du code Objective-C :

UIButton *monBouton = [UIButton buttonWithType:UIButtonTypeInfoDark ];
[monBouton setCenter:CGPointMake(100.0f, 100.0f)];
[self.view addSubview: monBouton];

La première instruction définit l'objet monBouton, le rattache à la classe UIButton et définit son type UIButtonTypeInfoDark.
La deuxième instruction définit les coordonnées du centre du bouton. Le premier nombre correspond à l'abscisse et le deuxième à l'ordonnée, par rapport au coin supérieur gauche de l'écran.
Enfin, la troisième instruction ajoute le bouton à la vue courante.

Si le bouton est de type Rounded Rect, vous pouvez également définir sa taille et le texte qui y est affiché :

UIButton *monBouton = [UIButton buttonWithType:UIButtonTypeRoundedRect ];
[monBouton setFrame:CGRectMake(100.0f, 100.0f, 100.0f, 20.0f)];
[monBouton setTitle: @"Mon bouton" forState: UIControlStateNormal];
[self.view addSubview: monBouton];

La première instruction définit l'objet monBouton, le rattache à la classe UIButton et définit son type UIButtonTypeInfoDark.
La deuxième instruction définit la position (les deux premiers paramètres) et les dimensions (les deux derniers paramètres) du bouton.
La troisième instruction définit le texte affiché dans le bouton.
Enfin, la quatrième instruction ajoute le bouton à la vue courante.

Les boutons relèvent de la classe UIButton. L'événement déclencheur par défaut est Touch Up Inside ; en d'autres termes, lorsqu'un doigt est enlevé du bouton. Si nécessaire, il est possible d'utiliser plusieurs autres déclencheurs. Par exemple Touch Down (lorsqu'un doigt est posé sur le bouton), Value Changed (modification du texte du bouton) ou encore Touch Drag Inside (appui sur le bouton et déplacement du doigt).

J'ai défini une action et une méthode événementielle pour mon bouton, mais lorsque j'appuie dessus, rien ne se passe. Est-ce que j'aurais raté quelque chose ?

Il y a de grandes chances pour que vous n'ayez pas relié le bouton et le code.
Contrôle-glissez-déposez le bouton de la zone d'édition sur le fichier d'en-têtes, juste au-dessus du @end. Au relâchement du bouton gauche de la souris, sélectionnez Action dans la liste Connection, donnez un nom à l'action et cliquez sur Connect.

Segmented Control

Les contrôles Segmented Control sont comparables à des onglets. Ils sont utilisés pour afficher ou cacher certains éléments en fonction de l'onglet (ou plutôt du « segment » dans le jargon Xcode) sélectionné. Ils sont attachés à la classe UISegmentedControl.
Supposons par exemple que vous ayez attaché plusieurs images à votre projet. En utilisant un Segmented Control, vous pouvez afficher une image pour chaque onglet. Nous allons mettre cela en pratique.

Définissez une nouvelle application basée sur le modèle Single View Application et donnez-lui le nom « onglets ». En utilisant Interface Builder, ajoutez au fichier MainStoryboard.storyboard un contrôle Segmented Control, un contrôle Image View et un contrôle Label. Positionnez et redimensionnez ces contrôles pour obtenir quelque chose comme la figure suivante.

Les contrôles doivent être placés de la sorte

Contrôle-glissez-déposez les contrôles de la zone d'édition sur le code du fichier d'en-têtes ViewController.h et définissez les connexions suivantes :

Contrôle

Type de liaison

Nom

Segmented Control

Outlet

onglets

Segmented Control

Action

ongletChange

Image View

Outlet

image

Label

Outlet

sousTitre

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

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UISegmentedControl *onglets;
- (IBAction)ongletChange:(id)sender;
@property (weak, nonatomic) IBOutlet UIImageView *image;
@property (weak, nonatomic) IBOutlet UILabel *sousTitre;
@end

Cette première étape effectuée, vous allez ajouter un onglet au Segmented Control et redéfinir le nom des trois onglets. Commencez par sélectionner le Segmented Control dans le canevas, cliquez sur l'icône Show the Attributes inspector dans la partie supérieure du volet des utilitaires. Inscrivez « 3 » dans la propriété Segments et appuyez sur la touche Entrée de votre clavier. Le contrôle comporte maintenant trois onglets.

Double-cliquez successivement sur chaque onglet dans la zone d'édition et renommez-les « Premier », « Deuxième » et « Troisième ». Si les onglets se recouvrent l'un l'autre, redimensionnez le Segmented Control pour arranger les choses. L'interface doit maintenant ressembler à la figure suivante.

L'interface de l'application

Définissez un dossier « Resources » et ajoutez trois images de 320x480 pixels dans ce dossier. Si cette manipulation vous échappe, reportez-vous à la section "Afficher une image" du chapitre 2 de la partie III. À la figure suivante se trouvent les trois images que j'ai utilisées. Elles proviennent de la bibliothèque de cliparts de Microsoft Office.

Les trois images utilisées dans l'exemple
Image utilisateur
Image utilisateur

Il est temps d'écrire quelques lignes de code. Aucune image n'ayant été affectée au contrôle Image View, ni aucun texte au contrôle Label, nous allons le faire via la méthode viewDidLoad.

Cliquez sur ViewController.m dans le volet de navigation et modifiez la méthode viewDidLoad comme suit :

- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *imageCourante = [UIImage imageNamed: @"cheval.jpg"];
[image setImage: imageCourante];
sousTitre.text = @"Un cheval et sa cavalière";
}

La ligne 4 définit l'objet UIImage imageCourante (UIImage *imageCourante) et lui affecte l'image « cheval.jpg » qui se trouve dans les ressources de l'application (= [UIImage imageNamed: @"cheval.jpg"]):

UIImage *imageCourante = [UIImage imageNamed: @"cheval.jpg"];

La ligne 5 affecte l'objet imageCourante au contrôle imageView image, et provoque son affichage :

[image setImage: imageCourante];

Enfin, la ligne 6 affiche un texte dans le Label :

sousTitre.text = @"Un cheval et sa cavalière";

Pour terminer l'application, il ne reste plus qu'à réagir aux changements d'onglets en affichant les images et le texte correspondants. Déplacez-vous dans la partie inférieure du code et complétez la méthode ongletChange comme suit :

- (IBAction)ongletChange:(id)sender {
if (onglets.selectedSegmentIndex == 0)
{
UIImage *imageCourante = [UIImage imageNamed: @"cheval.jpg"];
[image setImage: imageCourante];
sousTitre.text = @"Un cheval et sa cavalière";
}
if (onglets.selectedSegmentIndex == 1)
{
UIImage *imageCourante = [UIImage imageNamed: @"chien.jpg"];
[image setImage: imageCourante];
sousTitre.text = @"Un chien";
}
if (onglets.selectedSegmentIndex == 2)
{
UIImage *imageCourante = [UIImage imageNamed: @"chat.jpg"];
[image setImage: imageCourante];
sousTitre.text = @"Un chat";
}
}

Cette méthode est appelée chaque fois que l'utilisateur change d'onglet. Pour savoir quel onglet a été sélectionné, il suffit de lire la valeur stockée dans la propriété selectedSegmentIndex du contrôle Segmented Index (ici onglets). Cette propriété vaut 0 lorsque le premier onglet a été sélectionné, 1 lorsque le deuxième onglet a été sélectionné, 2 lorsque le troisième onglet a été sélectionné, et ainsi de suite.
Comme vous pouvez le remarquer, la méthode ongletChange est composée de trois blocs d'instructions à peu près similaires. Vous pouvez exécuter le projet.

À la figure suivante se trouve le résultat que j'ai obtenu.

Le résultat obtenu

Zones de texte

Les contrôles Text Field sont des zones de texte monolignes librement éditables par l'utilisateur. Lorsque l'utilisateur clique dans une zone de texte, le clavier intégré s'affiche dans la partie inférieure de l'écran, comme à la figure suivante.

Un clavier apparaît dans la zone inférieure de l'écran

Lorsque l'utilisateur a terminé la saisie, il appuie sur la touche Return du clavier intégré. Ce dernier doit alors disparaître. Pour cela, plusieurs actions sont nécessaires.

Dans le fichier .h

Supposons que votre application soit basée sur le modèle Single View Application, qu'elle ait pour nom tf (comme Text Field) et que son canevas contienne un contrôle Text Field. Vous allez ajouter la déclaration qui apparaît ligne 5 dans le fichier ViewController.h :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
- (IBAction)saisieReturn :(id)sender;
@end
Dans le fichier .m

Définissez le code relatif à la méthode saisieReturn dans le fichier ViewController.m :

- (IBAction)saisieReturn :(id)sender
{
[sender resignFirstResponder];
}

Le message [sender resignFirstResponder]; indique à son destinataire qu'il n'est plus le premier répondant. Si cette méthode est reliée à l'événement Did End on Exit de la zone de texte, le clavier disparaîtra, puisque la zone de texte n'aura plus le focus. Dans la prochaine étape, nous allons donc relier l'événement Did End on Exit et la méthode saisieReturn.

Dans Interface Builder

Comme à la figure suivante :

  • cliquez sur MainStoryboard.storyboard dans le volet de navigation (1) ;

  • cliquez sur la zone de texte dans la vue (2) ;

  • affichez le volet des utilitaires en cliquant sur Hide or Show the Utilities (3) ;

  • affichez les connexions en cliquant sur Show the Connections Inspector (4) ;

  • repérez le bouton radio à droite de Did End On Exit, pointez-le, maintenez le bouton gauche de la souris enfoncé et déplacez la souris sur l'icône View Controller (5) ;

  • au relâchement du bouton gauche de la souris, sélectionnez saisieReturn dans le menu (6).

Il faut relier l'événement Did End on Exit et la méthode saisieReturn

Pour récupérer le contenu d'une zone de texte, il suffit d'utiliser sa propriété text. Nous allons illustrer cette propriété en définissant une mini-application composée d'une zone de texte et d'un label, comme à la figure suivante.

Voici à quoi doit ressembler notre application

Pour obtenir ce résultat, commencez par définir une application basée sur le modèle Single View Application, ajoutez un contrôle Text Field et un contrôle Label au canevas.

L'application n'a pas encore l'apparence souhaitée. Vous devez modifier deux paramètres dans l'inspecteur des attributs :

  • cliquez sur le contrôle Text Field et écrivez « --- Entrez du texte ici --- » dans le paramètre Placeholder ;

  • cliquez sur le contrôle Label et tapez « Aucun texte n'a été entré » dans le paramètre Text.

Pour les étourdis, je rappelle que l'inspecteur des attributs est affiché en cliquant respectivement sur l'icône Hide or show the Utilities et Show the Attributes inspector. Il suffit alors de modifier l'attribut souhaité .

Vous allez maintenant relier ces deux contrôles au code de l'application :

  • Cliquez sur l'icône Show the Assistant editor dans la barre d'outils de Xcode.

  • Contrôle-glissez-déposez le contrôle Text Field du canevas dans le code source du fichier ViewController.h et créez l'outlet saisie.

  • Contrôle-glissez-déposez le contrôle Label du canevas dans le code source du fichier ViewController.h et créez l'outlet status.

Après ces manipulations, le fichier ViewController.h devrait contenir les instructions suivantes :

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

Nous allons supposer que l'effacement du clavier a été mis en place lorsque l'utilisateur appuie sur la touche Return, comme indiqué précédemment. Une ligne supplémentaire dans la méthode saisieReturn va suffire pour afficher le contenu de la zone de texte dans le Label (ici la ligne 3) :

- (IBAction)saisieReturn :(id)sender
{
status.text = [NSString stringWithFormat: @"%@%@", @"Vous avez tapé : ",saisie.text];
[sender resignFirstResponder];
}

La propriété text du contrôle Label (status.text) est initialisée avec l'objet NSString retourné par le message qui suit le signe « = ». Cet objet est constitué par la concaténation (c'est-à-dire l'ajout) de deux textes (stringWithFormat: @"%@%@") : la chaîne « Vous avez tapé » (@"Vous avez tapé : ") et le contenu du contrôle Text Field (saisie.text).

Curseurs

Les contrôles Slider (figure suivante) sont des curseurs horizontaux dont la position est ajustable par l'utilisateur. Vous les utiliserez pour faciliter la sélection de valeurs dans des plages.

Un contrôle Slider

Ces contrôles relèvent de la classe UISlider. Il est possible de les personnaliser dans Interface Builder en définissant :

  • une image pour représenter la valeur minimale et une autre pour représenter la valeur maximale ;

  • les valeurs minimales et maximales ;

  • la valeur par défaut au lancement de l'application.

La position du curseur est accessible à tout moment dans la propriété value.

Pour illustrer le fonctionnement de ce contrôle, nous allons créer une mini-application qui affiche dans un Label la position d'un Slider, et ce à chaque modification du curseur par l'utilisateur.

Définissez un nouveau projet en utilisant le modèle Single View Application et donnez-lui le nom « slider ». Ajoutez un Slider et un Label au canevas. Reliez le Label au fichier .h ; pour cela, effectuez un contrôle-glisser-déposer du Label dans le fichier .h et donnez le nom status à l'outlet ainsi créé.

Reliez le Slider au fichier .h. Pour cela, effectuez un contrôle-glisser-déposer du Slider dans le fichier .h. Au relâchement du bouton gauche de la souris, sélectionnez Action dans la liste Connection, choisissez Value Changed dans la liste Event et tapez « sl » dans la zone de texte Name.

Le fichier .h doit maintenant ressembler à ceci :

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

Pour compléter ces définitions, vous devez définir quelques lignes de code dans la méthode sl qui, rappelons-le, réagit à l'événement Value Changed du Slider :

- (IBAction)sl:(id)sender {
UISlider *slider = (UISlider *)sender;
status.text = [NSString stringWithFormat:@"%1.2f", slider.value];
}

La ligne 2 définit l'objet slider de type UISlider et le relie au Slider qui est à l'origine de l'événement.
La ligne 3 convertit la propriété value du Slider (slider.value) en un objet NSString (NSString stringWithFormat:@"%1.2f", …). L'objet ainsi obtenu est placé dans la propriété text du Label status (status.text = …), ce qui provoque l'affichage de la valeur du curseur dans le label.

Interrupteurs

Vous utiliserez un contrôle Switch chaque fois qu'il est nécessaire de mettre en place un interrupteur marche/arrêt, comme celui utilisé dans les réglages du device pour le mode avion (figure suivante).

Un contrôle Switch pour le mode avion

Le contrôle Switch relève de la classe UISwitch. La propriété booléenne on permet de connaître son état et la méthode setOn de le modifier. Voici la syntaxe de cette méthode :

- (void)setOn:(BOOL)on animated:(BOOL)animated

Donnez la valeur :

  • YES ou NO au paramètre on selon que vous vouliez mettre l'interrupteur sur ON ou sur OFF.

  • YES ou NO au paramètre animated selon que vous vouliez changer l'état de l'interrupteur ; c'est-à-dire avec ou sans animation.

Pour illustrer l'utilisation de ce contrôle, nous allons définir une mini-application qui affiche l'état d'un interrupteur marche/arrêt chaque fois que celui-ci change.

Définissez un nouveau projet en utilisant le modèle Single View Application et donnez-lui le nom « switch ». Ajoutez un Switch et un Label au canevas.

Reliez le Label au fichier .h en effectuant un contrôle-glisser-déposer du Label dans le fichier .h et donnez le nom « status » à l'outlet ainsi créé.

Reliez le Switch au fichier .h en effectuant un contrôle-glisser-déposer du Switch dans le fichier .h. Au relâchement du bouton gauche de la souris, sélectionnez Action dans la liste Connection, choisissez Value Changed dans la liste Event et tapez « sw » dans la zone de texte Name.

Le fichier .h doit maintenant ressembler à ceci :

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

Pour compléter ces définitions, vous devez définir quelques lignes de code dans la méthode sw qui, rappelons-le, réagit à l'événement Value Changed du Switch. Cliquez sur ViewController.m dans le volet de navigation puis modifiez la méthode sw comme suit :

- (IBAction)sw:(id)sender {
UISwitch *uis = (UISwitch *) sender;
if (uis.on == TRUE)
status.text = @"Le switch est sur ON";
else
status.text = @"Le switch est sur OFF";
}

La deuxième ligne définit l'objet uis de classe UISwitch et le relie au switch qui est à l'origine de l'événement.
La ligne suivante teste la valeur de la propriété on du switch. Si cette propriété vaut TRUE, cela signifie que l'interrupteur vient d'être initialisé à ON. Le texte « Le switch est sur ON » est alors affiché dans le label :

status.text = @"Le switch est sur ON";

Dans le cas contraire, cela signifie que l'interrupteur vient d'être initialisé à OFF. Le texte « Le switch est sur OFF » est alors affiché dans le label :

else
status.text = @"Le switch est sur OFF";

Contrôles de page

Les contrôles Page Control sont utilisés dans une vue qui comporte plusieurs pages. Ils permettent à l'utilisateur :

  1. de savoir où se situe la page courante dans l'ensemble des pages ;

  2. de se déplacer dans l'ensemble des pages.

Ces contrôles relèvent de la classe UIPageControl. Pour vous montrer comment les utiliser, nous allons développer une application dans laquelle un contrôle Scroll View contient des zones colorées mises bout à bout. Comme vous pouvez le voir sur l'image suivante, le Scroll View est bien plus large que l'écran de l'iPhone : il comporte cinq zones colorées de la même taille que l'écran.

Le contrôle Scroll View contient des zones colorées mises bout à bout

Définissez une nouvelle application basée sur le modèle Single View Application et donnez-lui le nom « page ». En utilisant Interface Builder, ajoutez un contrôle Scroll View au fichier MainStoryboard.storyboard, et redimensionnez-le pour qu'il occupe la quasi-totalité de l'écran.

Contrôle-glissez-déposez le contrôle Scroll View de la zone d'édition dans le code du fichier d'en-têtes ViewController.m et définissez l'outlet sv. Le fichier d'en-têtes doit maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIScrollView *sv;
@end

Nous allons maintenant définir les rectangles colorés qui seront affichés dans le contrôle Scroll View.

Pour qu'un rectangle représente une page dans le Scroll View, il suffit de lui donner la même taille que le Scroll View. Pour cela, nous allons utiliser la méthode viewDidLoad.
Cliquez sur ViewController.m dans le volet de navigation et complétez la méthode viewDidLoad comme ceci :

- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *couleurs = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], [UIColor cyanColor], [UIColor yellowColor],nil];
for (int i = 0; i < couleurs.count; i++)
{
// Définition d'un rectangle
CGRect rectangle;
rectangle.origin.x = sv.frame.size.width * i;
rectangle.origin.y = 0;
rectangle.size = sv.frame.size; //Le rectangle a la même dimension que le UIScrollView
// Ajout de la vue correspondante
UIView *subview = [[UIView alloc] initWithFrame:rectangle];
subview.backgroundColor = [couleurs objectAtIndex:i];
[sv addSubview:subview];
}
sv.contentSize = CGSizeMake(sv.frame.size.width * couleurs.count, sv.frame.size.height);
}

La ligne 4 définit un objet NSArray nommé couleurs (NSArray *couleurs) et l'initialise avec cinq couleurs prédéfinies.

Le bloc d'instructions suivant (lignes 5 à 17) définit les cinq rectangles et les transforme en vues du contrôle Scroll View. Pour bien faire les choses, la boucle for utilise le nombre de couleurs définies dans le tableau couleurs (couleurs.count) comme borne supérieure. Ainsi, si vous voulez définir plus ou moins de couleurs, l'application fonctionnera tout aussi bien :

for (int i = 0; i < couleurs.count; i++)
{
...
}

Les rectangles sont des objets CGRect. La première instruction de la boucle (ligne 8) commence par définir un objet rectangle de type CGRect :

CGRect rectangle;

Les trois instructions suivantes définissent l'origine et la taille du rectangle :

rectangle.origin.x = sv.frame.size.width * i;
rectangle.origin.y = 0;
rectangle.size = sv.frame.size;

Remarquez la façon dont est définie l'abscisse (rectangle.origin.x) du rectangle :

rectangle.origin.x = sv.frame.size.width * i;

La propriété frame.size.width de l'objet UIScrollView sv donne la largeur de ce contrôle. En la multipliant par l'index de la boucle, qui vaut consécutivement 0, 1, 2, 3 puis 4, on obtient les valeurs suivantes :

Index

rectangle.origin.x

0

0

1

Largeur de sv

2

2 largeurs de sv

3

3 largeurs de sv

4

4 largeurs de sv

Les cinq rectangles colorés seront donc placés côte à côte horizontalement.

Les instructions suivantes (lignes 14 à 16) définissent les différentes vues qui composent l'objet Scroll View. Pour cela, un objet subview de classe UIView est défini (UIView *subview) et initialisé avec le rectangle créé quelques lignes plus haut (initWithFrame:rectangle) :

UIView *subview = [[UIView alloc] initWithFrame:rectangle];

La couleur de cet objet est alors initialisée avec la couleur définie dans l'élément d'index i du tableau :

subview.backgroundColor = [couleurs objectAtIndex:i];

La sous-vue est enfin définie en utilisant l'objet subview :

[sv addSubview:subview];

Vous pouvez lancer l'application et constater (oh merveille !) qu'il est possible de scroller horizontalement dans le Scroll View.
Vous allez maintenant ajouter un contrôle Page Control au fichier ViewController.xib. Par défaut, le nombre de pages accessibles via un Page Control est égal à trois. Dans notre cas, nous devons scroller à travers cinq pages. Il faut donc modifier le nombre de pages par défaut. Cliquez sur le Page Control dans la zone d'édition, affichez le volet des attributs (si nécessaire en cliquant sur Hide or show the Utilities puis sur Show the Attributes inspector) et modifiez la valeur de l'attribut Pages, comme indiqué à la figure suivante.

Il faut modifier le nombre de pages accessibles par défaut

Définissez un outlet et une action pour le contrôle Page Control et nommez-les (respectivement) « laPage » et « changePage ». Le fichier d'en-têtes doit maintenant ressembler à ceci :

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIScrollView *sv;
@property (weak, nonatomic) IBOutlet UIPageControl *laPage;
- (IBAction)changePage:(id)sender;
@end

Pour que le Page Control puisse être mis à jour lorsque l'utilisateur change de page en utilisant une gestuelle de glisser, il est nécessaire d'être informé de cette gestuelle. Pour cela, nous déléguerons cette tâche au Scroll View. Il est donc nécessaire :

  1. d'ajouter le protocole UIScrollViewDelegate dans le contrôleur de vue, c'est-à-dire dans le fichier ViewController.h ;

  2. de connecter le delegate du Scroll View au contrôleur de vue.

À quoi vont servir ces deux étapes au juste ? Je ne suis pas sûr de bien comprendre.

Elles vont permettre d'écrire dans le code du contrôleur de vue (ViewController.m) les méthodes événementielles en rapport avec le contrôle Scroll View. En effet, en ajoutant le delegate UIScrollViewDelegate au contrôleur de vue et en le reliant au contrôleur de vue, ce dernier sera capable de traiter les événements du contrôle Scroll View.

La première étape se fait en spécifiant le protocole dans la déclaration de l'interface :

@interface ViewController : UIViewController <UIScrollViewDelegate>
{
...
}

La deuxième étape se fait en cliquant sur le contrôle Scroll View dans le canevas (1), puis en glissant-déposant le cercle à droite de delegate sur l'icône View Controller (2), comme à la figure suivante.

Un contrôle-glisser-déposer  sur l'icône View Controller

Retournons au code.
Cliquez sur ViewController.m dans le volet de navigation.

Le contrôleur de vue étant le délégué du Scroll View, nous allons utiliser une méthode de ce dernier pour mettre à jour le Page Control. Insérez la méthode suivante à un endroit quelconque dans le code. Par exemple juste avant la méthode viewDidLoad :

- (void)scrollViewDidScroll:(UIScrollView *)sender {
CGFloat largeurPage = sv.frame.size.width;
int page = floor((sv.contentOffset.x - largeurPage / 2) / largeurPage) + 1;
laPage.currentPage = page;
}

Cette méthode est exécutée lorsque l'utilisateur déplace horizontalement le Scroll View.
La ligne 2 stocke la largeur d'une page (c'est-à-dire celle du Scroll View) dans la variable CGFloat largeurPage :

CGFloat largeurPage = sv.frame.size.width;

La ligne 3 calcule le numéro de la page affichée dans le Scroll View. Ce numéro change lorsque la vue est décalée de plus de la moitié de l'écran (figure suivante).

Le numéro de page change lorsque la vue est décalée de plus de la moitié de l'écran

Le calcul paraît complexe, mais il n'en est rien. La propriété sv.ContentOffset.x représente le décalage de l'élément affiché dans le Scroll View. Si on lui soustrait la moitié de la largeur de la page, et qu'on divise le résultat par la largeur de la page, on obtient :

  • 0 lors du scroll de la page 1 à la page 2

  • 1 lors du scroll de la page 2 à la page 3

  • etc.

En ajoutant 1 à cette valeur, on obtient exactement ce qui est recherché, à savoir le numéro de la page vers laquelle l'utilisateur se déplace.

Toujours dans le flou ? Passons à une application numérique. Nous allons supposer que le device utilisé est un iPhone 3G. Bien sûr, ce raisonnement fonctionne sur tous les autres devices, mais il fallait bien en choisir un pour passer à l'application numérique.

La résolution d'un iPhone 3G est de 320x480 pixels. Supposons que l'utilisateur soit en train d'effectuer une gestuelle pour passer du premier écran au deuxième (comme sur la figure précédente).
Examinons les valeurs des différents éléments contenus dans la formule floor((sv.contentOffset.x - largeurPage / 2) / largeurPage) + 1.

L'élément

va passer de ... à ...

sv.contentOffset.x

0 à 320

sv.contentOffset.x - largeurPage / 2

-160 à 160

(sv.contentOffset.x - largeurPage / 2) / largeurPage

-0.5 à 0.5

floor((sv.contentOffset.x - largeurPage / 2) / largeurPage)

-1 jusqu'au milieu de l'écran, 0 après

floor((sv.contentOffset.x - largeurPage / 2) / largeurPage) + 1

0 jusqu'au milieu de l'écran, 1 après

Libre à vous de décomposer les calculs pour le passage du deuxième au troisième écran, du troisième au quatrième et du quatrième au cinquième. Vous verrez, cela fonctionne.

Si vous vous demandez comment j'ai pu trouver cette formule, eh bien, je me suis demandé quel résultat je voulais obtenir et après deux ou trois essais infructueux, je suis arrivé à définir la bonne formule. Cette approche fonctionne bien pour toutes sortes de formules, qu'elles soient plus simples ou plus complexes…

Retournons au code de l'application. Pour mettre à jour en conséquence le Page Control, il suffit d'affecter la valeur que l'on vient de calculer à sa propriété currentPage :

laPage.currentPage = page;

Pour terminer, nous allons compléter la méthode action changePage pour réagir aux actions de l'utilisateur sur le Page Control. En effet, pour le moment, ce contrôle est juste utilisé pour afficher la page active, mais pas pour changer de page. Rassurez-vous : le code sera bien plus simple que le précédent.

Localisez la méthode changePage et complétez-la comme suit :

- (IBAction)changePage:(id)sender {
CGRect frame;
frame.origin.x = sv.frame.size.width * laPage.currentPage;
frame.origin.y = 0;
frame.size = sv.frame.size;
[sv scrollRectToVisible:frame animated:YES];
}

La ligne 2 définit la variable frame de type CGRect. Le code se poursuit en définissant l'abscisse et l'ordonnée de l'affichage. Si vous n'avez qu'une vague idée de ce que représentent ces termes mathématiques, la figure suivante va vous rappeler de vieux souvenirs.

Abscisse et ordonnée

La ligne 3 définit le décalage en abscisse (frame.origin.x) de l'affichage :

frame.origin.x = sv.frame.size.width * laPage.currentPage;

Ce décalage est calculé en multipliant le numéro de la page courante par la largeur d'une page.

L'ordonnée de l'affichage est toujours égale à zéro :

frame.origin.y = 0;

Et la propriété size du rectangle est mise à jour avec les composantes x et y qui viennent d'être calculées :

frame.size = sv.frame.size;

Il ne reste plus qu'à mettre à jour l'affichage dans le Scroll View en définissant le rectangle visible (scrollRectToVisible:frame) et en demandant une animation (animated:YES) :

[sv scrollRectToVisible:frame animated:YES];

Vous pouvez (enfin) exécuter l'application et profiter du résultat !

En résumé

  • Les boutons sont les contrôles d'action de base. Leur apparence peut être personnalisée en utilisant Interface Builder ou des instructions Objective-C. Vous pouvez par exemple choisir la forme, le texte, l'image, la couleur d'arrière-plan et encore beaucoup d'autres caractéristiques.

  • Les contrôles Segmented Control sont comparables à des onglets. Ils sont utilisés pour afficher ou cacher certains éléments en fonction de l'onglet (ou plutôt du « segment » dans le jargon Xcode) sélectionné. Ils sont attachés à la classe UISegmentedControl.

  • Les contrôles Text Field sont des zones de texte monolignes librement éditables par l'utilisateur. Lorsque l'utilisateur clique dans un Text Field, le clavier intégré s'affiche dans la partie inférieure de l'écran.

  • Les contrôles Slider sont des curseurs horizontaux dont la position est ajustable par l'utilisateur. Vous les utiliserez pour faciliter la sélection de valeurs dans des plages.

  • Vous utiliserez un contrôle Switch chaque fois qu'il est nécessaire de mettre en place un interrupteur marche/arrêt, comme celui utilisé dans les réglages du device pour le mode avion.

  • Les contrôles Page Control sont utilisés dans une vue qui comporte plusieurs pages. Ils permettent à l'utilisateur de savoir où se situe la page courante dans l'ensemble des pages et de se déplacer dans l'ensemble des pages.

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