Points clés
UIGestureRecognizer est une classe de base abstraite dans UIKit qui décompose le processus de reconnaissance des gestes en objets séparés. Chaque sous-classe gère un type de geste : UITapGestureRecognizer gère un tap avec un nombre spécifique de touches, UISwipeGestureRecognizer gère un swipe dans une direction donnée. Le développeur ajoute le reconnaisseur à UIView via la méthode addGestureRecognizer(:), et UIKit suit automatiquement les touches, met à jour l'état et appelle une action lors de la reconnaissance.
Avant UIGestureRecognizer (iOS 3.2, 2010), les développeurs redéfinissaient les méthodes UIResponder — touchesBegan, touchesMoved, touchesEnded — et analysaient manuellement les trajectoires des touches. Cela entraînait une duplication de code et des erreurs lors du traitement des touches simultanées. Apple a encapsulé cette logique dans UIGestureRecognizer, ajoutant la prise en charge du multitouch, de l'annulation des gestes et du fonctionnement simultané de plusieurs reconnaisseurs sur une même vue.
Selon les sessions WWDC, Gesture Recognizer gère jusqu'à 11 touches simultanées sur iPad et 5 sur iPhone. Chez IT Sectr, nous utilisons UIGestureRecognizer comme méthode standard de gestion des entrées utilisateur dans tous les projets UIKit — cela a éliminé les bugs associés au suivi manuel de touchesBegan.
Chaque UIGestureRecognizer passe par 7 états possibles, définis dans l'énumération UIGestureRecognizer.State. Ces états reflètent le cycle de vie de la reconnaissance : de la détection de la touche à l'achèvement ou à l'annulation. Comprendre les états est essentiel pour implémenter des reconnaisseurs personnalisés et déboguer les conflits.
| État | Signification | Quand il se produit |
|---|---|---|
| .possible | État initial, geste pas encore reconnu | Immédiatement après l'ajout à une vue |
| .began | Geste reconnu et commencé à s'exécuter | Au premier mouvement du doigt pour pan/longPress |
| .changed | Paramètres du geste modifiés (coordonnées, angle) | À chaque mouvement du doigt |
| .ended | L'utilisateur a levé le doigt, geste terminé | À touchesEnded |
| .cancelled | Geste interrompu par le système (appel entrant, changement d'orientation) | À touchesCancelled |
| .failed | Geste non reconnu selon les conditions | À touchesCancelled sans reconnaissance |
| .recognized | Synonyme de .ended ; geste reconnu avec succès | Identique à .ended |
Les gestes discrets (tap, swipe) passent de .possible directement à .ended ou .failed. Les gestes continus (pan, pinch, rotation, longPress) parcourent .possible → .began → .changed (répété) → .ended. Dans la méthode d'action, vérifiez gestureRecognizer.state — cela permet de distinguer le début, le changement et la fin d'un geste.
UIKit fournit 7 sous-classes intégrées de UIGestureRecognizer, couvrant la plupart des scénarios d'interaction. Chaque sous-classe a des paramètres spécifiques : numberOfTapsRequired pour le tap, direction pour le swipe, minimumPressDuration pour la pression longue.
Pour les gestes personnalisés (par exemple, dessiner un zigzag), créez une sous-classe de UIGestureRecognizer en redéfinissant touchesBegan, touchesMoved, touchesEnded et en mettant à jour l'état. Apple recommande d'utiliser les classes intégrées lorsque c'est possible — elles sont optimisées et interagissent correctement entre elles.
Lorsque plusieurs UIGestureRecognizer sont utilisés sur une même UIView (par exemple, tap et double tap), un conflit de reconnaissance se produit : lors d'un double tap, le tap simple se déclenche en premier. Apple fournit la méthode require(toFail:) pour retarder la reconnaissance d'un geste jusqu'à ce qu'un autre échoue.
Le mécanisme fonctionne ainsi : en appelant tapRecognizer.require(toFail: doubleTapRecognizer), vous spécifiez que tapRecognizer passera à l'état .recognized seulement après que doubleTapRecognizer se termine en .failed. Cela ajoute un délai d'environ 0,3 seconde avant l'exécution du tap simple — l'utilisateur tape deux fois et le premier tap est ignoré. Une approche alternative est le délégué UIGestureRecognizerDelegate avec la méthode gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), qui permet la reconnaissance simultanée (par exemple, pan + pinch pour une carte).
Selon la WWDC 2020, environ 15% des bugs dans les applications UIKit sont liés à une configuration incorrecte des conflits de gestes. Chez IT Sectr, nous avons standardisé l'approche : chaque écran a un schéma de gestes avec des priorités require(toFail:) — cela a complètement éliminé les bugs de double déclenchement des taps.
Ajoute un gestionnaire de tap simple à UIImageView. Au tap, l'opacité de l'image change — un exemple simple montrant la liaison d'un geste à une vue.
import UIKit
class ImageViewController: UIViewController {
@IBOutlet private var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap(_:))
)
tap.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tap)
imageView.isUserInteractionEnabled = true
}
@objc private func handleTap(_: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.2) {
self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
}
}
}
Point clé : isUserInteractionEnabled sur UIImageView est false par défaut — sans ce drapeau, Gesture Recognizer ne recevra pas les touches. Pour UIView et UIButton, ce drapeau est activé par défaut. La méthode d'action accepte un paramètre UITapGestureRecognizer, via lequel on peut obtenir location(in:) pour déterminer les coordonnées du tap.
Implémente un balayage vers la gauche pour revenir à l'écran précédent. Montre la configuration de direction et la liaison du geste à la vue racine du contrôleur.
import UIKit
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swipeLeft = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipe(_:))
)
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
}
@objc private func handleSwipe(_: UISwipeGestureRecognizer) {
navigationController?.popViewController(animated: true)
}
}
UISwipeGestureRecognizer est un geste discret : il passe à .recognized immédiatement après la reconnaissance, sans états .changed intermédiaires. Par conséquent, il n'est pas nécessaire de vérifier l'état dans l'action — le geste est soit reconnu (l'action se déclenche), soit non. La propriété direction accepte l'une des quatre valeurs : .left, .right, .up, .down. Pour prendre en charge plusieurs directions, créez des reconnaisseurs séparés pour chacune.
Montre comment configurer le tap simple et double sur une même vue sans conflit. Le reconnaisseur de double tap a la priorité — le tap simple se déclenche seulement si le double tap n'est pas reconnu.
import UIKit
class TapViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleSingleTap)
)
singleTap.numberOfTapsRequired = 1
let doubleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleDoubleTap)
)
doubleTap.numberOfTapsRequired = 2
singleTap.require(toFail: doubleTap)
view.addGestureRecognizer(singleTap)
view.addGestureRecognizer(doubleTap)
}
@objc private func handleSingleTap() {
print("Single tap — after 0.3s delay")
}
@objc private func handleDoubleTap() {
print("Double tap — instant")
}
}
Sans require(toFail:), lors d'un double tap, handleSingleTap se déclenche en premier, puis handleDoubleTap — cela brise l'UX. Avec require(toFail:), le tap simple attend environ 0,3 seconde pour s'assurer qu'un second tap ne suit pas. Chez IT Sectr, ce modèle est utilisé dans les éditeurs d'images et les galeries, où un double tap zoome et un tap simple sélectionne un élément.
Questions fréquentes
Oui, UIView prend en charge plusieurs instances de UIGestureRecognizer simultanément. Pour résoudre les conflits, utilisez la méthode require(toFail:) qui définit l'ordre de reconnaissance. Pour le fonctionnement parallèle des gestes (par exemple, pan + pinch sur une carte), implémentez la méthode déléguée gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) retournant true.
UIGestureRecognizer est une abstraction de haut niveau qui reconnaît automatiquement les motifs de toucher et gère les états. touchesBegan est une méthode de bas niveau de UIResponder qui nécessite un suivi manuel des coordonnées, un minutage et une annulation des touches. Gesture Recognizer est plus simple, plus fiable et préférable pour les gestes standard ; touchesBegan n'est justifié que pour les graphismes personnalisés.
SwiftUI natif utilise des modificateurs : onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Ce sont des analogues déclaratifs de UIGestureRecognizer intégrés dans la hiérarchie SwiftUI. Si nécessaire, on peut envelopper un reconnaisseur UIKit via UIViewRepresentable, mais Apple recommande d'utiliser les gestes natifs de SwiftUI.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi