12

Contact et gestuelle

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.

Détection de doigt(s) au contact de l’écran

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.

La classe UITouch

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éthodes déclenchées par les événements liés aux mouvements des doigts

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.

image

Figure 12–1 Les différents éléments appelés lors d’un contact

Cycle de vie d’un contact à l’écran

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"

image - (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);

}

}

image - (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);

}

}

image - (void)touchesEnded:(NSSet *)touches

withEvent:(UIEvent *)event {

NSLog(@"Fin de la présence d’un doigt à l’écran");

}

image - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {

NSLog(@"Interruption par un programme extérieur…");

}

image 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.

image 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.

image Enfin, lorsque les doigts sont retirés de l’écran, la méthode touchesEnded: est invoquée, ou touchesCancelled: image 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.

Prise en charge des gestes courants (discrets et continus)

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.

Tableau 12-1 Gestes courants pris en charge par défaut par iOS
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.

Taper à l’écran

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:.

image

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.

Faire glisser le doigt (swipe)

Pour détecter un effet de glissement à l’écran, provoquant une action, on procède de façon identique.

image

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.

Faire pivoter un objet

Pour faire pivoter un élément, il va falloir impérativement placer deux doigts à l’écran.

image

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];

}

Déplacer (pan)

Pour déplacer un élément, on effectue une pression continue et le déplacement du doigt emporte avec lui l’élément.

image

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];

}

Pression longue

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

image

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];

}

Pincer/zoomer (pinch)

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

image

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];

}

Gestes simultanés

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;

}

Conclusion

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>