Pinch-to-Zoom — comprendre le geste de pincement dans les applications mobiles

Auteur : IT Sectr Publié le : 2026-02-27 Temps de lecture : 8 min
Pinch-to-Zoom (pincement des doigts) est un geste multitactile de zoom dans lequel l'utilisateur rapproche ou écarte deux doigts sur l'écran pour réduire ou agrandir le contenu. Ce geste est devenu le standard de facto pour visualiser des images, des cartes, des pages web et des documents sur les appareils mobiles. Selon Apple HIG et Google Material Design, Pinch-to-Zoom est un geste de base que les utilisateurs attendent dans toutes les applications travaillant avec du contenu visuel.

Points clés

  • Pinch-to-Zoom est un geste multitactile à deux doigts pour mettre à l'échelle le contenu sur les appareils mobiles.
  • Sous iOS, le traitement est effectué via UIPinchGestureRecognizer avec un suivi continu de .began → .changed → .ended.
  • Android utilise ScaleGestureDetector (de la Support Library) avec le callback onScale pour mettre à jour le zoom via Matrix.
  • UIPinchGestureRecognizer possède les propriétés scale (rapport par rapport à l'état précédent) et velocity (vitesse).
  • Pour limiter le zoom, un clamp du facteur d'échelle est appliqué entre les valeurs min/max définies (généralement 0,5–3,0).

Qu'est-ce que Pinch-to-Zoom?

Pinch-to-Zoom (pincement) est un geste multitactile qui utilise deux doigts pour modifier l'échelle du contenu affiché. En rapprochant les doigts (pinch close), l'échelle diminue ; en les écartant (pinch open), elle augmente. Le geste est continu : le système suit la distance entre les doigts en temps réel et génère des événements intermédiaires, permettant à l'application de mettre à jour l'échelle en douceur.

La mécanique du geste repose sur le calcul de la distance entre deux points de contact. iOS et Android utilisent la distance euclidienne : sqrt((x2−x1)² + (y2−y1)²). L'échelle actuelle est calculée comme le rapport entre la distance actuelle et la distance initiale. Si la distance initiale était de 100px et devient 150px — échelle = 1,5 (augmentation de 50%). iOS transmet l'échelle relative à l'état précédent (delta), Android transmet l'échelle accumulée via ScaleGestureDetector.

Selon les Google Material Design Guidelines, Pinch-to-Zoom est obligatoire pour les applications qui affichent des images, des cartes ou des documents. Chez IT Sectr, nous implémentons ce geste avec UIPinchGestureRecognizer et ScaleGestureDetector dans tous les projets qui travaillent avec du contenu visuel — des galeries aux éditeurs d'images.

Pinch-to-Zoom sous iOS : UIPinchGestureRecognizer

UIPinchGestureRecognizer est une sous-classe continue de UIGestureRecognizer pour reconnaître le geste de pincement. Contrairement aux reconnaisseurs discrets (UITapGestureRecognizer), il génère une séquence d'événements : .began (les doigts touchent l'écran), .changed (chaque mouvement des doigts), .ended (les doigts sont levés).

Propriétés clés : scale (CGFloat) — changement relatif de la distance entre les doigts depuis le début du geste ; velocity (CGFloat) — taux de changement d'échelle par seconde. Échelle initiale = 1,0. En écartant les doigts, l'échelle augmente (1,0 → 1,5 → 2,0) ; en les rapprochant, elle diminue (1,0 → 0,8 → 0,5). La propriété velocity est utilisée pour l'inertie : après .ended, vous pouvez animer la poursuite de la mise à l'échelle avec décélération.

UIPinchGestureRecognizer prend en charge la propriété delegate pour la reconnaissance simultanée avec d'autres reconnaisseurs (par exemple, pan + pinch sur une carte). La méthode gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) retourne true, et les deux gestes fonctionnent en parallèle. Apple recommande d'ajouter UIPinchGestureRecognizer à la vue conteneur plutôt qu'à des éléments individuels — cela simplifie le calcul du point pivot de la mise à l'échelle.

Pinch-to-Zoom sous Android : ScaleGestureDetector

ScaleGestureDetector est une classe de la Android Support Library (android.view) conçue pour reconnaître la mise à l'échelle multitactile. Contrairement à GestureDetector.onFling, ScaleGestureDetector est spécifiquement conçu pour le zoom à deux doigts et calcule automatiquement focusX, focusY (le point central entre les doigts) et scaleFactor (delta d'échelle de l'événement précédent).

ScaleGestureDetector fonctionne via l'interface OnScaleGestureListener avec trois méthodes : onScaleBegin(detector) (analogue à .began), onScale(detector) (analogue à .changed, retourne un booléen — true si l'échelle est acceptée), onScaleEnd(detector) (analogue à .ended). Propriétés : scaleFactor — multiplicateur d'échelle relatif à l'événement précédent (~1,01 pour un écartement lent), focusX/focusY — coordonnées du point central entre les doigts.

Pour appliquer la mise à l'échelle à une View, on utilise généralement Matrix : view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix prend en charge l'accumulation des transformations — chaque appel postScale ajoute une nouvelle transformation à celle existante. Il est important de se rappeler : ScaleGestureDetector ne limite pas l'échelle minimale/maximale — c'est la responsabilité du développeur. Chez IT Sectr, nous utilisons ScaleGestureDetector avec des limites d'échelle personnalisées (0,5–3,0) et une animation de retour en cas de dépassement.

Limitation du zoom et point pivot

Lors de l'implémentation de Pinch-to-Zoom, il est essentiel de limiter l'échelle minimale et maximale — sans cela, l'utilisateur pourrait zoomer à l'infini ou dézoomer jusqu'à l'invisibilité. Valeurs standard : min = 0,5 (50% de la taille d'origine), max = 3,0 (300%). Sous iOS, la limitation se fait via clamp : newScale = min(max(currentScale * gesture.scale, minScale), maxScale). Sous Android — via une vérification après postScale.

Le point pivot est le centre entre les doigts, par rapport auquel la mise à l'échelle est effectuée. iOS calcule automatiquement le centroïde des deux touches et le transmet via location(in:) — le développeur n'a pas besoin de calculer le pivot manuellement. Android fournit focusX/focusY dans ScaleGestureDetector, mais lors de l'application via Matrix, le pivot est passé comme troisième et quatrième arguments dans postScale(scaleFactor, scaleFactor, focusX, focusY). Sans point pivot, la mise à l'échelle sera effectuée par rapport au coin supérieur gauche (0,0), ce qui déplace le contenu lors du zoom.

D'après l'expérience d'IT Sectr, le calcul correct du point pivot est la source la plus fréquente de bugs lors de l'implémentation de Pinch-to-Zoom. Dans 90% des cas, il suffit d'utiliser les coordonnées intégrées de GestureRecognizer et ScaleGestureDetector — pas besoin de calculer le pivot manuellement. Si le contenu se déplace lors de la mise à l'échelle, vérifiez si vous passez focusX/focusY à matrix.postScale().

Inertie après le zoom

Pour une expérience utilisateur fluide après la fin du geste, vous pouvez ajouter de l'inertie : sous iOS — utilisez velocity de UIPinchGestureRecognizer avec UIView.animate et des paramètres spring ; sous Android — OverScroller ou ValueAnimator. L'inertie ne doit pas dépasser les limites d'échelle min/max — la valeur finale est limitée avec une animation de retour spring en cas de dépassement.

Exemples de code en Swift et Kotlin

Exemple 1 : Pinch-to-Zoom sous iOS (Swift)

Ajoute UIPinchGestureRecognizer à UIImageView avec une limitation d'échelle de 0,5 à 3,0. Montre une implémentation minimale avec conservation de l'échelle accumulée.

swift
import UIKit

class ZoomableImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!
    private var currentScale: CGFloat = 1.0
    private let minScale: CGFloat = 0.5
    private let maxScale: CGFloat = 3.0

    override func viewDidLoad() {
        super.viewDidLoad()

        let pinch = UIPinchGestureRecognizer(
            target: self,
            action: #selector(handlePinch(_:))
        )
        imageView.addGestureRecognizer(pinch)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handlePinch(_: UIPinchGestureRecognizer) {
        if gestureRecognizer.state == .began
            || gestureRecognizer.state == .changed {

            let newScale = currentScale * gestureRecognizer.scale
            imageView.transform = CGAffineTransform(
                scaleX: min(max(newScale, minScale), maxScale),
                y: min(max(newScale, minScale), maxScale)
            )
        }
        if gestureRecognizer.state == .ended {
            currentScale = min(max(
                currentScale * gestureRecognizer.scale,
                minScale
            ), maxScale)
            gestureRecognizer.scale = 1.0
        }
    }
}

Logique : dans .began/.changed, l'échelle est appliquée via CGAffineTransform avec clamp. Dans .ended, l'échelle est réinitialisée à 1,0 et la valeur accumulée est stockée dans currentScale. Cela évite la dérive de l'échelle lors de gestes répétés — chaque nouveau geste commence à partir de l'état actuel. Pour les images, UIScrollView avec minimumZoomScale/maximumZoomScale est recommandé plutôt que CGAffineTransform.

Exemple 2 : Pinch-to-Zoom sous Android (Kotlin)

Implémente la mise à l'échelle d'ImageView via ScaleGestureDetector avec Matrix. Montre la gestion correcte du point pivot et la limitation de l'échelle.

kotlin
import android.content.Context
import android.graphics.Matrix
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.widget.ImageView

class PinchZoomImageView(context: Context) : ImageView(context) {

    private val matrix = Matrix()
    private var currentScale = 1f
    private val minScale = 0.5f
    private val maxScale = 3.0f

    private val scaleDetector =
        ScaleGestureDetector(context, ScaleListener())

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            currentScale = (currentScale * detector.scaleFactor)
                .coerceIn(minScale, maxScale)
            matrix.postScale(
                currentScale,
                currentScale,
                detector.focusX,
                detector.focusY
            )
            imageMatrix = matrix
            return true
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleDetector.onTouchEvent(event)
        return true
    }
}

L'ImageView personnalisée remplace onTouchEvent, transmettant les événements à ScaleGestureDetector. OnScaleListener.onScale() est appelé à chaque mouvement des doigts. detector.scaleFactor est le delta (1,01 pour un écartement lent). currentScale s'accumule avec coerceIn pour le clamp. focusX/focusY — le point central entre les doigts, passé à postScale pour une mise à l'échelle correcte du pivot.

Exemple 3 : UIScrollView avec prise en charge du zoom (iOS)

UIScrollView dispose d'une prise en charge intégrée de Pinch-to-Zoom via son délégué. C'est l'approche préférée pour les images et les documents — ScrollView gère automatiquement l'échelle, l'inertie et le défilement.

swift
class ScrollableImageViewController: UIViewController,
                                    UIScrollViewDelegate {

    @IBOutlet private var scrollView: UIScrollView!
    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.minimumZoomScale = 0.5
        scrollView.maximumZoomScale = 3.0
        scrollView.delegate = self
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
}

UIScrollView crée automatiquement un UIPinchGestureRecognizer, gère la mise à l'échelle via la transformation CALayer et ajoute un effet de rebond lors du dépassement des limites. Le code se réduit à définir min/maxZoomScale et à retourner viewForZooming. Chez IT Sectr, nous utilisons UIScrollView pour toutes les images en plein écran — cela réduit le volume de code de 5 fois par rapport à l'implémentation manuelle via UIPinchGestureRecognizer.

Questions fréquentes

En quoi le pinch diffère-t-il du pincement sur un trackpad?

Sur un écran tactile, pinch utilise deux doigts avec suivi de la distance entre eux. Sur un trackpad MacBook, le zoom est implémenté via UIPinchGestureRecognizer avec les mêmes états (.began → .changed → .ended), mais la source tactile est un trackpad avec Force Touch, pas un écran capacitif. L'API est la même, la mécanique est identique.

Comment limiter le zoom minimal et maximal?

Sous iOS : après avoir obtenu l'échelle de UIPinchGestureRecognizer, multipliez par currentScale et appliquez clamp(min: 0.5, max: 3.0). Sous Android : utilisez coerceIn(minScale, maxScale) avant matrix.postScale(). Dans UIScrollView — via les propriétés minimumZoomScale et maximumZoomScale. Sans limitation, l'échelle peut aller à l'infini ou à zéro.

Pinch-to-Zoom fonctionne-t-il avec un seul doigt?

Non, le pinch nécessite au moins deux doigts. Pour le zoom à un doigt (double tap + glisser vers le haut/bas), une implémentation personnalisée via UIPanGestureRecognizer ou GestureDetector.onScroll est nécessaire. C'est un geste différent avec une logique différente — généralement utilisé pour le zoom dans l'appareil photo ou les cartes.

Résumé

  • Pinch-to-Zoom est un geste multitactile à deux doigts pour mettre à l'échelle le contenu, le standard pour les images, les cartes et les documents.
  • UIPinchGestureRecognizer sous iOS est un reconnaisseur continu avec scale (delta) et velocity (vitesse).
  • ScaleGestureDetector sous Android est un détecteur spécialisé avec onScale(), focusX/focusY pour le point pivot.
  • Pour UIScrollView, le zoom est intégré via minimumZoomScale/maximumZoomScale — plus simple que l'implémentation manuelle.
  • Limitez toujours l'échelle (0,5–3,0) via clamp — sans cela, le contenu peut devenir invisible.
  • Le point pivot de la mise à l'échelle est le centre entre les doigts ; sans lui, le contenu se déplace lors du zoom.
  • Chez IT Sectr, nous utilisons UIScrollView pour les images et ScaleGestureDetector personnalisé pour les éditeurs — le choix dépend de la complexité des transformations.

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.

Discuter du projet

Lisez aussi