Points clés
Le balayage est un mouvement de glissement rapide du doigt sur la surface de l'écran, se terminant par un retrait. Un balayage peut être court (feuilleter un élément) ou long (ouvrir un rideau), mais la caractéristique clé est la discrétion : le système le reconnaît comme un événement unique, pas comme une séquence continue de coordonnées.
Les systèmes d'exploitation mobiles détectent un balayage selon trois paramètres : distance minimale (seuil), vitesse minimale (velocity) et direction. Si le doigt se déplace lentement ou si la distance est insuffisante, le système interprète le geste comme un panoramique ou un toucher. Les valeurs de seuil diffèrent entre les plateformes : iOS utilise une constante dans UISwipeGestureRecognizer avec un seuil fixe, Android utilise ViewConfiguration.getScaledTouchSlop(), qui retourne ~8–16 dp selon la densité de l'écran.
Selon la Documentation développeurs Android, onFling (balayage) dans GestureDetector se déclenche à des vitesses supérieures à 100 px/s et des distances à partir de 50px. Chez IT Sectr, nous utilisons le balayage pour la navigation (galeries, interfaces cartes, onboarding) — ce geste est intuitif pour les utilisateurs de tous âges.
Le balayage et le panoramique (pan/drag) sont des types de gestes différents, bien que les deux impliquent de déplacer un doigt sur l'écran. Le balayage est discret et signifie "effectuer une action", tandis que le panoramique est continu et signifie "déplacer un objet tant que le doigt est sur l'écran".
| Paramètre | Balayage (Swipe) | Panoramique (Pan/Drag) |
|---|---|---|
| Type | Discret | Continu |
| Événements | Unique (reconnu/non reconnu) | Multiples (.began → .changed → .ended) |
| Vitesse | Élevée (mouvement rapide) | Quelconque (mouvement lent) |
| Exemple iOS | UISwipeGestureRecognizer | UIPanGestureRecognizer |
| Exemple Android | GestureDetector.onFling() | GestureDetector.onScroll() |
| Scénario | Supprimer un email, tourner une page | Déplacer une carte, glisser un fichier |
| Suivi des coordonnées | Non (direction seulement) | Oui (mise à jour constante des coordonnées) |
Le choix entre balayage et panoramique dépend de la tâche. Pour les actions sur un élément (supprimer, archiver, ouvrir un menu), utilisez le balayage. Pour la manipulation directe (déplacer, redimensionner), utilisez le panoramique. Mélanger les gestes sur le même élément nécessite require(toFail:) ou une logique personnalisée.
UISwipeGestureRecognizer est une sous-classe de UIGestureRecognizer qui reconnaît un balayage discret dans l'une des quatre directions. Contrairement à UIPanGestureRecognizer, il ne génère pas d'événements .changed intermédiaires : une fois reconnu, l'action est appelée exactement une fois.
La propriété direction (UISwipeGestureRecognizer.Direction) définit la direction suivie — .left, .right, .up, .down. Les directions peuvent être combinées via un masque : [.left, .right] pour les balayages horizontaux. La propriété numberOfTouchesRequired détermine le nombre de doigts — 1 par défaut. UISwipeGestureRecognizer n'a pas de seuil de distance configurable — iOS utilise une valeur intégrée d'environ 20px, suffisante pour une reconnaissance fiable.
Comme UISwipeGestureRecognizer est un reconnaisseur discret, il n'entre pas en conflit avec UIPanGestureRecognizer : un mouvement lent déclenche pan, un mouvement rapide déclenche swipe. Cependant, en coexistence avec UITapGestureRecognizer sur la même vue, require(toFail:) peut être nécessaire pour éviter une fausse reconnaissance de tap.
Sous Android, l'API principale pour la reconnaissance de balayage est GestureDetector avec le callback OnGestureListener, spécifiquement la méthode onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY). GestureDetector analyse la position initiale et finale du doigt, la vitesse de déplacement sur les axes et la distance, puis appelle onFling lorsque les seuils sont dépassés.
Les seuils minimaux sont configurables via ViewConfiguration : VelocityTracker.getMinVelocity() (~100 px/s) et ViewConfiguration.getScaledTouchSlop() (~8–16 dp). Un OnSwipeTouchListener personnalisé est un modèle courant qui simplifie l'API : le développeur n'a pas besoin de calculer manuellement la direction et la vitesse. La Android Support Library ne contient pas de "SwipeDetector" intégré, donc un wrapper personnalisé est la solution standard.
Pour le balayage dans les listes, Google recommande d'utiliser ItemTouchHelper.SimpleCallback de RecyclerView, qui abstrait la détection des gestes, l'animation et les callbacks onSwiped(). Chez IT Sectr, nous utilisons un SwipeListener personnalisé pour ViewPager2 et les interfaces cartes — cela donne un contrôle sur la sensibilité du balayage sans sacrifier les performances.
Pour les listes (UITableView, RecyclerView), Apple et Google fournissent des API spécialisées qui remplacent l'ajout manuel de UISwipeGestureRecognizer. Ces API prennent en charge les boutons d'action, les animations personnalisées et les gestes "balayer pour supprimer" — des modèles standard dans les applications mobiles.
Sous iOS (11+), on utilise UISwipeActionsConfiguration : la méthode tableView(_:leadingSwipeActionsConfigurationForRowAt:) retourne un tableau de UIContextualAction — des boutons avec titre, style (destructive, normal) et gestionnaire. Par exemple, "Supprimer" — .destructive avec fond rouge, "Archiver" — .normal avec fond gris. Le système anime l'apparition des boutons et appelle le bloc completion après un tap.
Sous Android, ItemTouchHelper.SimpleCallback est attaché à RecyclerView via ItemTouchHelper.attachToRecyclerView(). Les paramètres incluent des drapeaux de direction (start, end, up, down) et le callback onSwiped(viewHolder, direction). Dans onSwiped, adapter.notifyItemRemoved(position) est appelé pour supprimer l'élément avec animation. ItemTouchHelper gère automatiquement les gestes, les conflits de défilement et le retour de l'élément si le balayage n'est pas terminé. Selon Google I/O 2024, ItemTouchHelper est utilisé dans 70% des applications Android avec des actions de balayage dans les listes.
Ajoute UISwipeGestureRecognizer à une vue pour revenir à l'écran précédent. Le code montre la configuration minimale du reconnaisseur avec la direction .left.
import UIKit
class CardViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swipeLeft = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipeLeft(_:))
)
swipeLeft.direction = .left
swipeLeft.numberOfTouchesRequired = 1
view.addGestureRecognizer(swipeLeft)
}
@objc private func handleSwipeLeft(_: UISwipeGestureRecognizer) {
guard let nav = navigationController else { return }
if nav.viewControllers.count > 1 {
nav.popViewController(animated: true)
} else {
nav.dismiss(animated: true)
}
}
}
Le reconnaisseur est attaché à la vue racine du contrôleur. La vérification navigationController.viewControllers.count garantit que pop n'est exécuté que s'il y a un écran précédent dans la pile. Pour les contrôleurs modaux, dismiss est utilisé. Le code ne nécessite pas de vérification d'état — UISwipeGestureRecognizer est discret et appelle l'action uniquement après une reconnaissance réussie.
Implémente un wrapper simple de GestureDetector qui détermine la direction du balayage et appelle les callbacks onSwipeLeft/Right.
import android.content.Context
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
open class OnSwipeTouchListener(context: Context) : View.OnTouchListener {
private val gestureDetector = GestureDetector(context, GestureListener())
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
companion object {
private const val SWIPE_THRESHOLD = 100
private const val SWIPE_VELOCITY_THRESHOLD = 100
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffX = e2.x - e1!!.x
val diffY = e2.y - e1.y
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
) {
if (diffX > 0) onSwipeRight()
else onSwipeLeft()
return true
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD
&& Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD
) {
if (diffY > 0) onSwipeDown()
else onSwipeUp()
return true
}
}
return false
}
}
open fun onSwipeLeft() {}
open fun onSwipeRight() {}
open fun onSwipeUp() {}
open fun onSwipeDown() {}
}
OnSwipeTouchListener étend SimpleOnGestureListener, implémentant onFling. diffX/diffY déterminent la direction, SWIPE_THRESHOLD (100px) et SWIPE_VELOCITY_THRESHOLD (100 px/s) sont les seuils. Utilisation : view.setOnTouchListener(OnSwipeTouchListener(context).apply { onSwipeLeft = { dismiss() } }).
Ajoute un bouton de suppression lors du balayage vers la gauche sur une cellule de tableau. Utilise l'API intégrée iOS 11+ sans reconnaissance manuelle des gestes.
func tableView(
_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(
style: .destructive,
title: "Supprimer"
) { _, _, completionHandler in
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
completionHandler(true)
}
deleteAction.backgroundColor = .systemRed
let config = UISwipeActionsConfiguration(actions: [deleteAction])
config.performsFirstActionWithFullSwipe = true
return config
}
La méthode retourne UISwipeActionsConfiguration avec un tableau de UIContextualAction. style: .destructive définit automatiquement le fond rouge. performsFirstActionWithFullSwipe = true permet d'exécuter l'action avec un balayage complet sans tap supplémentaire. Chez IT Sectr, nous utilisons cette API pour toutes les listes avec actions — elle est standardisée, prend en charge VoiceOver et ne nécessite pas de GestureRecognizers personnalisés.
Foire aux questions
Le balayage est un geste discret avec mouvement rapide et retrait du doigt, le panoramique est un glissement continu avec suivi des coordonnées. UISwipeGestureRecognizer se déclenche une fois, UIPanGestureRecognizer génère .began → .changed → .ended. Le choix dépend du scénario : balayage pour les actions (supprimer, tourner), pan pour le déplacement (carte, curseur).
La logique du geste est identique, mais l'API diffère. iOS utilise UISwipeGestureRecognizer avec direction. Android utilise GestureDetector.onFling() avec des seuils personnalisés. Le résultat est la même UX avec des implémentations différentes. Les frameworks multiplateformes (Flutter, React Native) abstraient la différence via une API de balayage unifiée.
Sous iOS (11+), utilisez UISwipeActionsConfiguration dans UITableViewDelegate. Sous Android, utilisez ItemTouchHelper.SimpleCallback pour RecyclerView. Ces API remplacent les GestureRecognizers manuels, prennent en charge les boutons d'action et l'animation, gèrent correctement les conflits de défilement et s'adaptent aux HIG de la plateforme.
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