Pour le développeur sous iOS, la détection et la gestion des actions de l’utilisateur sur l’écran – notamment les actions à plusieurs doigts (multi-touch) – est incroyablement facile.
Deux mécanismes sont mis à la disposition du développeur pour obtenir des informations sur les actions de l’utilisateur. Le premier indique de manière brute et continue à l’application combien de doigts sont posés et leur position sur l’écran.
Un second niveau d’abstraction est proposé avec les détecteurs de gestes (gesture recognizer). Ils reconnaissent très simplement les gestes habituels de l’utilisateur (pincer, glisser, taper, appui long) ou même de nouvelles gestuelles que vous voudriez inventer.
La détection de la présence d’un doigt au contact de l’écran et de son mouvement jusqu’à ce qu’il quitte l’écran est gérée par quatre événements de la classe UIResponder
. Cette classe abstraite est héritée par toutes les classes susceptibles de traiter des événements : les vues, les contrôleurs de vue et le délégué de l’application.
En général, dans vos applications, vous recevez ces événements dans vos vues spécifiques (de nouvelle classes qui héritent de UIView
) ou dans vos contrôleurs de vue.
Lorsque l’utilisateur pose un doigt sur l’écran, un événement de type « touche » est créé, c’est-à-dire un objet UITouch
dont la propriété type est UIEventTypeTouches.
Le déplacement d’un doigt provoque l’envoi d’événements qui sont passés au programme à travers la classe UITouch.
Cette dernière possède les propriétés suivantes :
• tapCount
: nombre de tapes consécutives effectuées par le doigt de l’utilisateur pendant un intervalle de temps court ;
• phase
: état du contact à l’écran (touch began, touch moved, touch remained stationery, touch ended
et touch cancelled
);
• timestamp
: horodatage de l’événement lié au contact ;
• view
: nom de la vue de type UIView
d’où le contact à l’écran provient ;
• window
: fenêtre de type UIWindow
d’où le contact à l’écran provient.
En plus de ces propriétés, on dispose de deux méthodes liées à la classe UITouch
:
• locationInView :
cette méthode fournit les coordonnées du contact sous la forme d’un objet de type CGPoint
;
• previousLocationInView :
coordonnées précédentes du contact sous la forme d’un objet de type CGPoint.
Les événements que l’appareil détecte et nous envoie font partie de la classe UIEvent.
Méthode | Description |
touchesBegan:withEvent: |
Méthode appelée lorsque au moins un doigt est détecté à l’écran. |
touchesMoved:withEvent: |
Méthode appelée lorsqu’un doigt se déplace à l’écran. |
touchesEnded:withEvent: |
Méthode appelée lorsque le doigt quitte l’écran. |
touchesCancelled:withEvent: |
Méthode appelée lorsque le déplacement du doigt est interrompu par un événement externe, par exemple un appel entrant sur un iPhone. |
Chaque événement renvoie toutes les instances de UITouch
en cours.
Figure 12–1 Les différents éléments appelés lors d’un contact
Après avoir créé un nouveau projet de type Single View Application, choisissez les options Universal, Using Automatic Reference Counting et Use Storyboards.
Pour chaque storyboard, sélectionnez l’option Multiple Touch dans l’inspecteur d’attributs (Attributes Inspector) à droite de l’écran.
Dans le fichier d’implémentation (.m
), on a le code suivant :
#pragma mark - Détection des "touches"
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"Nombre de doigts détectés: %i", [touches count]);
NSInteger compteur = 1;
for (UITouch *touch in touches) {
CGPoint position = [touch locationInView:self.view];
NSLog(@"Touche %i, x: %0.1f, y:%0.1f",
compteur++, position.x, position.y);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger compteur = 1;
for (UITouch *touch in touches) {
CGPoint position = [touch locationInView:self.view];
NSLog(@"Touche %i, x: %0.1f, y:%0.1f",
compteur++, position.x, position.y);
}
}
- (void)touchesEnded:(NSSet *)touches
withEvent:(UIEvent *)event {
NSLog(@"Fin de la présence d’un doigt à l’écran");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"Interruption par un programme extérieur…");
}
Dans le simulateur ou sur un appareil, l’utilisateur pose un ou plusieurs doigt(s) à l’écran et déclenche la méthode
touchesBegan:.
Cette méthode parcourt le tableau de touches pour afficher toutes les coordonnées des doigts.
Puis lorsque les doigts se déplacent à l’écran, la méthode
touchesMoved:
est invoquée et met à jour la liste des touches à l’écran.
Enfin, lorsque les doigts sont retirés de l’écran, la méthode
touchesEnded:
est invoquée, ou touchesCancelled:
en cas d’interruption du contact par une autre application.
On voit dans la console les messages correspondant à l’apparition des doigts, du déplacement et à l’arrêt du contact à l’écran.
Le développeur a la possibilité de programmer sa propre gestuelle à l’aide des méthodes de contact précédemment citées. Cela dit, iOS met à sa disposition tous les mécanismes pour implémenter facilement le traitement des gestes les plus courants.
Geste | Description |
Glisser (swipe) | Le doigt se déplace d’un bord à l’autre de l’écran et fait défiler des objets, comme pour l’application Photos. |
Pivoter | Deux doigts pivotent et provoquent la rotation de l’objet sélectionné. |
Pincer/zoomer (pinch) | Zoomer sur un objet. |
Déplacer (pan) | Déplacer un objet le long de l’écran pour le placer à d’autres coordonnées. |
Pression longue | Pression longue déclenchant un événement. |
Tapotement (Tap) | Taper deux ou plusieurs fois à l’écran pour effectuer une action. |
Les gestes sont divisés en deux catégories :
• Les gestes discrets : l’événement se produit et déclenche aussitôt l’événement associé à la méthode (cas d’un double tap).
• Les gestes continus : l’événement se poursuit et fournit des informations jusqu’à son aboutissement (cas de la rotation d’un objet).
Les gestes discrets peuvent passer par les états suivants :
• UIGestureRecognizerStatePossible
• UIGestureRecognizerStateRecognized
• UIGestureRecognizerStateFailed
Les gestes continus passent par la série d’états suivants :
• UIGestureRecognizerStatePossible
• UIGestureRecognizerStateBegan
• UIGestureRecognizerStateChanged
• UIGestureRecognizerStateEnded
• UIGestureRecognizerStateFailed
Le fonctionnement des gestes obéit au mécanisme de programmation suivant :
1 On crée un objet correspondant au geste désiré.
2 On associe ce nouvel objet à la liste des gestes reconnus par la vue ou une sous-vue (comme un label).
3 On déclare la méthode qui sera appelée lorsque la vue sera sollicitée par l’action.
On peut décider de déclencher une action lorsque l’utilisateur tape plusieurs fois à l’écran. Cela ouvre plus de possibilités que la méthode tapCount
que nous avons déjà rencontrée plus haut dans la méthode touchesBegan:.
Figure 12–2 Tapoter à l’écran
Voici, à titre d’exemple, le code correspondant à la triple tape sur un label à l’écran.
Dans le fichier d’en-tête .h,
on déclare une propriété correspondant au geste voulu.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel*typeDeMouvement;
UILabel*labelDeTest;
// Tap
UITapGestureRecognizer*tapGestureRecognizer;
}
@property(nonatomic,strong) IBOutletUILabel*typeDeMouvement;
@property(nonatomic,strong) IBOutletUILabel*labelDeTest;
@property(nonatomic,strong) UITapGestureRecognizer
*tapGestureRecognizer;
@end
Dans le fichier d’implémentation .m,
le geste est associé à la vue ou à un objet descendant d’une vue (ici un label).
- (void)detectionTap {
self.typeDeMouvement.text= @"Tap détecté";
}
…
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
tapGestureRecognizer= [[UITapGestureRecognizeralloc]
initWithTarget:self
action:@selector(detectionTap)];
// Nombre de doigts minimum à l’écran
tapGestureRecognizer.numberOfTouchesRequired= 1;
// Nombre de tapes avant de déclencher le geste
tapGestureRecognizer.numberOfTapsRequired= 3;
[self.labelDeTestaddGestureRecognizer:tapGestureRecognizer];
}
Si on travaille avec un label par exemple, il faut penser à sélectionner les options suivantes en sélectionnant l’inspecteur d’attributs du label dans le storyboard.
On modifiera alors dans la partie Interaction :
• User Interaction Enabled ;
• Multi Touch.
On instancie le geste et on l’associe à la vue ou au label.
Dans cet exemple, il faut taper trois fois de suite pour déclencher le geste.
Pour détecter un effet de glissement à l’écran, provoquant une action, on procède de façon identique.
Figure 12–3 Faire glisser un doigt sur l’écran
Le fichier d’en-tête .h
possède les déclarations suivantes :
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel *nombreDeTouches;
UILabel *typeDeMouvement;
UILabel *labelDeTest;
// Glisser
UISwipeGestureRecognizer*swipeGestureRecognizer;
}
@property (nonatomic,strong) IBOutlet UILabel *nombreDeTouches;
@property (nonatomic,strong) IBOutlet UILabel *typeDeMouvement;
@property (nonatomic,strong) IBOutlet UILabel *labelDeTest;
@property(nonatomic,strong) UISwipeGestureRecognizer
*swipeGestureRecognizer;
@end
Dans le fichier d’implémentation .m,
le geste est associé à la vue ou à un objet descendant d’une vue. On continue à travailler avec notre label, ce qui permet d’associer plusieurs gestes au même objet.
- (void)detectionSwipe {
self.typeDeMouvement.text = @"Swipe détecté";
}
…
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
swipeGestureRecognizer= [[UISwipeGestureRecognizerall oc]
initWithTarget:self
action:@selector(detectionSwipe)];
swipeGestureRecognizer.direction=
UISwipeGestureRecognizerDi rectionLeft|
UISwipeGestureRecognizerDirectionRight;
[self.labelDeTest
addGestureRecognizer:swipeGestureRecognizer];
}
On peut reconnaître le geste dans plusieurs directions si on le désire en utilisant le OU logique (|) et en déclarant les directions suivantes :
• UISwipeGestureRecognizerDirectionRight;
• UISwipeGestureRecognizerDirectionLeft;
• UISwipeGestureRecognizerDirectionUp;
• UISwipeGestureRecognizerDirectionDown.
Notez qu’on peut combiner plusieurs gestes à un même objet.
Pour faire pivoter un élément, il va falloir impérativement placer deux doigts à l’écran.
Figure 12–4 Faire pivoter un élément à l’écran
On conserve l’angle du label dans une variable flottante.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel *nombreDeTouches;
UILabel *typeDeMouvement;
UILabel *labelDeTest;
// Pivoter
UIRotationGestureRecognizer*rotationGestureRecognizer;
CGFloatangle;
}
@property (nonatomic,strong) IBOutlet UILabel *nombreDeTouches;
@property (nonatomic,strong) IBOutlet UILabel *typeDeMouvement;
@property (nonatomic,strong) IBOutlet UILabel *labelDeTest;
@property(nonatomic,strong) UIRotationGestureRecognizer
*rotationGestureRecognizer;
@property(nonatomic,assign) CGFloatangle;
@end
Dans le fichier d’implémentation .m
, le geste est associé à la vue ou à un objet descendant d’une vue (ici notre éternel label).
- (void)detectionRotation:(UIRotationGestureRecognizer *)sender {
self.typeDeMouvement.text= @"Rotation détecté";
// Prendre l’angle et ajouter la rotation détectée
self.labelDeTest.transform=
CGAffineTransformMakeRotation(self.angle+ sender.rotation);
if(sender.state== UIGestureRecognizerStateEnded) {
self.angle+= sender.rotation;
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
// Pivoter
rotationGestureRecognizer= [[UIRotationGestureRecognizeralloc]
initWithTarget:self
action:@selector(detectionRotation:)];
[self.labelDeTest
addGestureRecognizer:rotationGestureRecognizer];
}
Pour déplacer un élément, on effectue une pression continue et le déplacement du doigt emporte avec lui l’élément.
Figure 12–5 Faire glisser un élément le long de l’écran
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel *nombreDeTouches;
UILabel *typeDeMouvement;
UILabel *labelDeTest;
// Glisser (pan)
UIPanGestureRecognizer*panGestureRecognizer;
}
@property (nonatomic,strong) IBOutlet UILabel *nombreDeTouches;
@property (nonatomic,strong) IBOutlet UILabel *typeDeMouvement;
@property (nonatomic,strong) IBOutlet UILabel *labelDeTest;
@property(nonatomic,strong) UIPanGestureRecognizer
*panGestureRecognizer;
@end
Dans le fichier d’implémentation .m
, le geste est associé à la vue ou à un objet descendant d’une vue (ici un label).
- (void)detectionPan:(UIPanGestureRecognizer *)sender {
self.typeDeMouvement.text= @"Pan détecté";
if(sender.state!= UIGestureRecognizerStateEnded&&
sender.state!= UIGestureRecognizerStateFailed) {
CGPointposition = [sender
locationInView:sender.view.superview];
sender.view.center = position;
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
// Glisser (pan)
panGestureRecognizer= [[UIPanGestureRecognizeralloc]
initWithTarget:self
action:@selector(detectionPan:)];
self.panGestureRecognizer.minimumNumberOfTouches= 1;
self.panGestureRecognizer.maximumNumberOfTouches= 1;
[self.labelDeTest
addGestureRecognizer:panGestureRecognizer];
}
La pression longue déclenche une action en fonction du nombre de doigts posés sur l’écran et du temps durant lequel ils sont présents.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel *nombreDeTouches;
UILabel *typeDeMouvement;
UILabel *labelDeTest;
// Pression longue
UILongPressGestureRecognizer*longPressRecognizer;
}
@property (nonatomic,strong) IBOutlet UILabel *nombreDeTouches;
@property (nonatomic,strong) IBOutlet UILabel *typeDeMouvement;
@property (nonatomic,strong) IBOutlet UILabel *labelDeTest;
@property(nonatomic,strong) UILongPressGestureRecognizer
*longPressRecognizer;
@end
Figure 12–6 Effectuer une pression longue sur l’écran
Dans le fichier d’implémentation .m
, le geste est associé à la vue ou à un objet descendant d’une vue (ici le label).
-(void)detectionLong {
self.typeDeMouvement.text= @"Long détecté";
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
// Pression longue
longPressRecognizer= [[UILongPressGestureRecognizeralloc]
initWithTarget:self
action:@selector(detectionLong)];
self.longPressRecognizer.numberOfTouchesRequired= 2;
self.longPressRecognizer.minimumPressDuration= 2.0f;
[self.labelDeTestaddGestureRecognizer:longPressRecognizer];
}
Le dernier geste est un pincement pour obtenir un effet de zoom à l’écran. Utilisé dans de nombreuses applications, il a grandement contribué au succès de l’iPhone.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
UILabel *nombreDeTouches;
UILabel *typeDeMouvement;
UILabel *labelDeTest;
// Pincer - pinch
UIPinchGestureRecognizer*pinchGestureRecognizer;
CGFloatechelle;
}
@property (nonatomic,strong) IBOutlet UILabel *nombreDeTouches;
@property (nonatomic,strong) IBOutlet UILabel *typeDeMouvement;
@property (nonatomic,strong) IBOutlet UILabel *labelDeTest;
@property(nonatomic,strong) UIPinchGestureRecognizer
*pinchGestureRecognizer;
@property(nonatomic,assign) CGFloatechelle;
@end
Figure 12–7 Pincer l’écran pour obtenir un effet de zoom
Dans le fichier d’implémentation .m
, le geste est associé à la vue ou à un objet descendant d’une vue (ici notre label).
-(void)detectionPinch:(UIPinchGestureRecognizer *)sender {
self.typeDeMouvement.text= @"Pinch détecté";
if(sender.state== UIGestureRecognizerStateBegan) {
self.echelle= 1.0;
}
CGFloatcetteEchelle = 1+ (sender.scale- self.echelle);
self.echelle= sender.scale;
sender.view.transform = CGAffineTransformScale(sender.view.transform,
cetteEchelle,
cetteEchelle);
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Départ.
//Pincer - Pinch
pinchGestureRecognizer= [[UIPinchGestureRecognizeralloc]
initWithTarget:self
action:@selector(detectionPinch:)];
[self.labelDeTest
addGestureRecognizer:pinchGestureRecognizer];
}
Si vous désirez implémenter plusieurs gestes simultanément, il ne suffit pas de les associer à un objet. En effet, iOS va prendre le premier geste detecté et se verrouiller dessus, conduisant à un rendu graphique inadapté.
Il suffit simplement d’ajouter la méthode suivante dans votre ViewController.
- (void)detectionTap {
#pragma mark - Méthode pour une gestion simultanée des gestes
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
…
Dans ce chapitre, nous avons vu comment utiliser la classe UITouch
pour gérer la détection des doigts à l’écran et de leur mouvement. Le suivi et la gestion des événements liés au déplacement sont pris en compte de façon très naturelle dans iOS.
À l’aide des classes appropriées, nous avons également vu comment associer un geste à un objet à l’écran.
Propriété de Marie Richie <FB1245387-C111754-prod@customers.feedbooks.com>