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 ;
- 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.
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 :
Code : Objective-C | #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 :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | - (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 :
Code : Objective-C | 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 :
Code : Objective-C
Les trois instructions suivantes définissent l'origine et la taille du rectangle :
Code : Objective-C | 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 :
Code : Objective-C | 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) :
Code : Objective-C | 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 :
Code : Objective-C | subview.backgroundColor = [couleurs objectAtIndex:i];
|
La sous-vue est enfin définie en utilisant l'objet
subview :
Code : Objective-C
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.
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 :
Code : Objective-C | #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 :
- d'ajouter le protocole UIScrollViewDelegate dans le contrôleur de vue, c'est-à-dire dans le fichier ViewController.h ;
- 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 :
Code : Objective-C | @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.
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 :
Code : Objective-C | - (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 :
Code : Objective-C | 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 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 :
Code : Objective-C | 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 :
Code : Objective-C | - (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.
La ligne 3 définit le décalage en abscisse (
frame.origin.x) de l'affichage :
Code : Objective-C | 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 :
Code : Objective-C
Et la propriété
size du rectangle est mise à jour avec les composantes
x et
y qui viennent d'être calculées :
Code : Objective-C | 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) :
Code : Objective-C | [sv scrollRectToVisible:frame animated:YES];
|
Vous pouvez (enfin) exécuter l'application et profiter du résultat !