Au-delà de la présentation et de l’utilisation des PickerViews, ce chapitre va revenir sur deux design patterns très importants pour structurer les échanges entre vos contrôleurs de vue et les vues : la délégation de contrôle et les sources de données. Ces deux mécanismes permettent la communication entre la vue et le contrôleur dans le modèle MVC.
Nous avons présenté au chapitre 3 le design pattern délégation de contrôle et un cas d’utilisation avec le délégué de l’application.
Le schéma page suivante résume le fonctionnement d’un PickerView :
• Après avoir alloué le PickerView dans la vue, le delegate
et le dataSource
sont positionnés.
• Le dataSource
va répondre à la méthode numberOfComponents:inPickerView :
et retourner le nombre de composants à allouer.
• Chacun de ces composants va invoquer à son tour la méthode pickerView:numberOfRows :inComponent :
pour renvoyer le nombre d’éléments pour chaque composant créé.
• Enfin, pour chaque ligne, la méthode pickerView:titleForRow:inComponent :
permettra de remplir les éléments avec du texte.
• Le delegate
déclenchera la méthode pickerView:didSelectRow:forComponent :
lorsque l’utilisateur effectuera une sélection, permettant de traiter l’élément sélectionné.
Figure 10–1 Fonctionnement d’un PickerView
Ce design pattern est également utilisé pour informer les objets de votre application des changements d’état de l’interface. Très souvent, les contrôleurs de vue de votre application seront les délégués des vues de votre interface. Il suffit de quelques étapes pour cela.
1 Indiquer dans le fichier de déclaration de votre contrôleur de vue que cette classe implémente le protocole du délégué.
@interface MyViewController :
UIViewController<UIPickerViewDelegate> {
2 Indiquer à votre vue (l’objet UIPickerView
, par exemple) que son délégué est le contrôleur de vue.
Ceci peut être fait de deux façons : dans l’interface graphique en liant la propriété delegate
au contrôleur de vue, ou dans la méthode viewDidLoad
en affectant à la propriété delegate
la valeur self
.
3 Dans le fichier d’implémentation, ajouter les méthodes obligatoires du délégué (la documentation du délégué précise toujours quelles sont les méthodes que vous devez implémenter et celles qui sont optionnelles).
- (void) pickerView:(UIPickerView *) pickerView
didSelectRow:(NSInteger) row
inComponent:(NSInteger) component)
{
// Traitement de l’événement.
}
Avec ce mécanisme, les méthodes de votre contrôleur de vue seront appelées à chaque fois qu’un événement sera reçu par une de vos vues.
Bien sûr, votre contrôleur peut être le délégué de plusieurs vues (vous pouvez par exemple avoir plusieurs UIPickerView
dans un même écran) et c’est pour cette raison que le premier paramètre de toutes les méthodes de délégués est l’objet qui est à l’origine de l’événement.
Le fonctionnement des sources de données est très proche de celui du design pattern de délégation de contrôle. Là encore, une de vos classes, un contrôleur de vue le plus souvent, implémente un protocole spécifique et indique à une vue quelle sera sa source de données, via la propriété datasource
.
Les méthodes du protocole sont ensuite appelées par la vue quand elle a besoin d’obtenir les données à afficher.
Il est possible de présenter plusieurs éléments UIPickerView
à l’écran. Chacun peut proposer plusieurs composants, comme le montre l’image suivante.
Figure 10–2 Exemple de UIPickerView sur iPad
Prenons un projet de type Single View Application.
Dans le fichier .h,
on indique :
• le UIPickerView
qui apparaîtra dans la vue ;
• la liste qui alimentera le UIPickerView.
Exemple de déclaration d’un UIPickerView
#import <UIKit/UIKit.h>
@interface ViewController :
UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>
{
// Le PickerView
UIPickerView *lePickerView;
// La liste d’éléments
NSMutableArray *liste;
}
@property(strong,nonatomic) IBOutlet UIPickerView *lePickerView;
@property(strong,nonatomic) NSMutableArray *liste;
@end
Dans le storyboard, ajoutez un élément graphique de type PickerView
et connectezle à l’outlet déclaré dans le fichier .h
. Sélectionnez le ViewController
dans lequel vous avez placé le PickerView
et connectez le composant à son nom (ici lePickerView
).
Pour indiquer au PickerView
quel est son délégué et quelle est sa source de données, sélectionnez le PickerView
et connectez les propriétés delegate
et datasource
au contrôleur de vue.
Il faut ensuite implémenter les trois méthodes obligatoires du protocole UIPickerViewDataSource
suivantes :
Méthode | Description |
- (NSInteger)numberOfComponentsInPickerView: (UIPickerView *)pickerView |
Méthode appelée à la création de tout PickerView : nombre de composants dans le PickerView. |
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component |
Méthode appelée à la création de tout composant dans un PickerView : nombre de lignes dans le composant. |
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component |
Méthode appelée à la création de chaque ligne du composant : on sélectionne l’élément de même indice dans le tableau. |
Et enfin voici la méthode du protocole UIPickerViewDelegate
appelée lors de la sélection d’un des choix :
Méthode | Description |
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)
|
Méthode appelée lors de la sélection d’un élément dans le PickerView. |
On obtient alors le code suivant.
Utilisation du PickerView – Fichier d’implémentation (.m
)
#import "ViewController.h"
@implementation ViewController
#pragma mark - Propriétés
@synthesize lePickerView;
@synthesize liste;
#pragma mark - Méthodes de UIPickerViewDataSource
// Nombre de composants dans le PickerView
- (NSInteger) numberOfComponentsInPickerView:
(UIPickerView *)pickerView
{
return 1; // Un seul composant dans le PickerView
}
// Nombre d’éléments dans le composant du PickerView
- (NSInteger) pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
// Le nombre d’éléments dans le PickerView est égal au nombre
// d’éléments dans la liste.
return [liste count];
}
- (NSString *) pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
// Renvoi le libellé de l’élément
return [liste objectAtIndex:row];
}
#pragma mark - Méthodes delegate du PickerView
- (void) pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
// Action lors de la sélection d’un élément
NSLog(@"Sélection de l’élément:%@ ",
[liste objectAtIndex:row]);
}
#pragma mark - View lifecycle
- (void) viewDidLoad
{
[super viewDidLoad];
// Préparation de la liste des éléments à afficher.
liste = [[NSMutableArray alloc] init];
[liste addObject:@"Un"];
[liste addObject:@"Deux"];
[liste addObject:@"Trois"];
[liste addObject:@"Quatre"];
}
On compile (Cmd-R) et on vérifie le résultat ; le composant PickerView est fonctionnel.
Que se passe-t-il maintenant lorsqu’il y a plusieurs PickerViews et plusieurs composants par PickerView ?
Il suffit de modifier les méthodes de la source de données et du délégué pour distinguer les éléments les uns des autres.
Prenons par exemple, deux PickerViews ; le premier est constitué d’un seul composant, le second de deux composants. On déclare le fichier d’en-tête suivant :
Exemple de PickerViews multiples dans le fichier .h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
<UIPickerViewDelegate, UIPickerViewDataSource> {
// Les deux PickerViews
UIPickerView *pickerView1;
UIPickerView *pickerView2;
// Les 3 listes - 1 pour PV1 2 pour PV2
NSMutableArray *liste1;
NSMutableArray *liste2;
NSMutableArray *liste3;
}
@property (strong,nonatomic) IBOutlet UIPickerView *pickerView1;
@property (strong,nonatomic) IBOutlet UIPickerView *pickerView2;
@property (strong,nonatomic) NSMutableArray *liste1;
@property (strong,nonatomic) NSMutableArray *liste2;
@property (strong,nonatomic) NSMutableArray *liste3;
@end
Dans le fichier d’implémentation (.m
), on trouve tout d’abord les propriétés qui correspondent aux variables d’instance de notre fichier d’en-tête puis les méthodes data source.
Propriétés dans le fichier d’implémentation .m
#import "ViewController.h"
@implementation ViewController
#pragma mark - propriétés
@synthesize pickerView1;
@synthesize pickerView2;
@synthesize liste1;
@synthesize liste2;
@synthesize liste3;
Selon que l’appel émane de pickerView1
ou de pickerView2,
la méthode retourne le nombre de composants voulus.
Méthode pour le nombre de composants dans le fichier d’implémentation .m
#pragma mark - DataSource pour les PickerView
// Nombre de composants à présenter
- (NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView {
NSInteger nombreDeComposants = 0;
if (pickerView == pickerView1) {
nombreDeComposants = 1;
} else if (pickerView == pickerView2) {
nombreDeComposants = 2;
}
return nombreDeComposants;
}
À la création de chaque composant, un appel à la méthode pickerView:numberOfRowsInComponent:
du délégué est effectué afin d’allouer le nombre correct d’éléments. On utilise la méthode count
dans notre exemple pour connaître le nombre d’éléments de chaque tableau.
Méthode pour le nombre d’élément par composant dans le fichier d’implémentation .m
// Nombre d’éléments dans le(s) composants(s)
- (NSInteger) pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
NSInteger nombreDElements = 0;
if (pickerView == pickerView1) {
nombreDElements = [liste1 count];
} else if (pickerView == pickerView2) {
if (component == 0) {
nombreDElements = [liste2 count];
} else if (component == 1) {
nombreDElements = [liste3 count];
}
}
return nombreDElements;
}
Chaque PickerView va faire appel à la méthode pickerView:titleForRow: forComponent:
de la source de données pour obtenir le libellé de chaque ligne de chaque composant.
Notez à nouveau comment est faite la distinction entre les appels des deux Picker-Views et des différents composants.
Méthode pour remplir les éléments dans le fichier d’implémentation .m
- (NSString *) pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
if (pickerView == pickerView1) {
return [liste1 objectAtIndex:row];
} else if (pickerView == pickerView2) {
if (component == 0) {
return [liste2 objectAtIndex:row];
} else if (component == 1) {
return [liste3 objectAtIndex:row];
}
}
// Pour éviter le warning, mais il n’est jamais atteint
return nil;
}
Le délégué peut implémenter la méthode optionnelle pickerView: widthForComponent:
pour indiquer la largeur de chacun des composants et avoir ainsi des composants de largeurs différentes.
Méthode pour la largeur des composants dans le fichier d’implémentation .m
- (CGFloat) pickerView:(UIPickerView *)pickerView
widthForComponent:(NSInteger)component
{
float largeur;
if (pickerView == pickerView1) {
largeur = 100.0f;
} else if (pickerView == pickerView2) {
switch (component) {
case 0:
largeur = 250.0f;
break;
case 1:
largeur = 100.0f;
break;
default:
break;
}
}
return largeur;
}
Lorsque l’utilisateur sélectionne un élément d’un PickerView, il déclenche un événement qui est récupéré par la méthode suivante :
Méthode delegate de sélection dans le fichier d’implémentation .m
#pragma mark - Delegate pour les PickerViews
// Action lorsque l’utilisateur a choisi une valeur
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
NSString *chaine =
[[NSString alloc] initWithFormat:@"-%@-%@-%@-",
[liste1 objectAtIndex:[pickerView1 selectedRowInComponent:0]],
[liste2 objectAtIndex:[pickerView2 selectedRowInComponent:0]],
[liste3 objectAtIndex:[pickerView2 selectedRowInComponent:1]]];
NSLog(@"Choix : %@", chaine);
}
Exemple de PickerViews multiples dans le fichier d’implémentation .m
- (void) viewDidLoad
{
[super viewDidLoad];
// Départ, initialisation des 3 listes.
// Liste 1 - PickerView1 - Composant 0
liste1 = [[NSMutableArray alloc] init];
[liste1 addObject:@"Un"];
[liste1 addObject:@"Deux"];
[liste1 addObject:@"Trois"];
// Liste 2 - PickerView2 - Composant 0
liste2 = [[NSMutableArray alloc] init];
[liste2 addObject:@"Rouge"];
[liste2 addObject:@"Vert"];
[liste2 addObject:@"Bleu"];
// Liste 3 - PickerView2 - Composant 1
liste3 = [[NSMutableArray alloc] init];
[liste3 addObject:@"Lion"];
[liste3 addObject:@"Tigre"];
[liste3 addObject:@"‘Éléphant"];
// Sélection de l’élément par défaut
[pickerView1 selectRow:2 inComponent:0 animated:NO];
}
Ce chapitre a illustré les design patterns délégation de contrôle et source de données, ainsi que leur utilisation pour mettre en œuvre les PickerViews.
En conclusion, lorsqu’il faut présenter un choix à l’utilisateur, on peut utiliser en fonction du nombre d’éléments :
• les commutateurs UISwitch
de type O/I ;
• les boutons segmentés pour 5 à 6 choix ;
• les PickerViews pour une cinquantaine de choix.
Le chapitre 11 présente l’étape suivante : les listes d’éléments (ou tableviews) qui offrent la plus grande souplesse dans une interface iOS. Un grand nombre d’applications y ont recours, des carnets de contacts aux recettes de cuisine…
Propriété de Marie Richie <FB1245387-C111754-prod@customers.feedbooks.com>