Pinch-to-Zoom — entendendo o gesto de pinça em aplicativos móveis

Autor: IT Sectr Publicado: 2026-02-27 Tempo de leitura: 8 min
Pinch-to-Zoom (pinça com os dedos) é um gesto multitoque de zoom no qual o usuário junta ou afasta dois dedos na tela para diminuir ou aumentar o conteúdo. Este gesto tornou-se o padrão de fato para visualizar imagens, mapas, páginas web e documentos em dispositivos móveis. De acordo com a Apple HIG e o Google Material Design, Pinch-to-Zoom é um gesto básico que os usuários esperam em todos os aplicativos que trabalham com conteúdo visual.

Pontos principais

  • Pinch-to-Zoom é um gesto multitoque com dois dedos para escalar conteúdo em dispositivos móveis.
  • No iOS, o gerenciamento é feito através do UIPinchGestureRecognizer com rastreamento contínuo de .began → .changed → .ended.
  • O Android usa ScaleGestureDetector (da Support Library) com o callback onScale para atualizar o zoom através de Matrix.
  • UIPinchGestureRecognizer possui as propriedades scale (razão relativa ao estado anterior) e velocity (velocidade).
  • Para limitar o zoom, aplica-se um clamp do fator de escala entre os valores mín/máx definidos (normalmente 0.5–3.0).

O que é Pinch-to-Zoom?

Pinch-to-Zoom (pinça) é um gesto multitoque que usa dois dedos para alterar a escala do conteúdo exibido. Ao juntar os dedos (pinch close), a escala diminui; ao afastá-los (pinch open), aumenta. O gesto é contínuo: o sistema rastreia a distância entre os dedos em tempo real e gera eventos intermediários, permitindo que o aplicativo atualize suavemente a escala.

A mecânica do gesto baseia-se no cálculo da distância entre dois pontos de toque. iOS e Android usam a distância euclidiana: sqrt((x2−x1)² + (y2−y1)²). A escala atual é calculada como a razão entre a distância atual e a distância inicial. Se a distância inicial era 100px e se torna 150px — scale = 1.5 (aumento de 50%). O iOS passa a escala relativa ao estado anterior (delta), o Android passa a escala acumulada através do ScaleGestureDetector.

De acordo com as Google Material Design Guidelines, o Pinch-to-Zoom é obrigatório para aplicativos que exibem imagens, mapas ou documentos. Na IT Sectr, implementamos este gesto com UIPinchGestureRecognizer e ScaleGestureDetector em todos os projetos que trabalham com conteúdo visual — de galerias a editores de imagem.

Pinch-to-Zoom no iOS: UIPinchGestureRecognizer

UIPinchGestureRecognizer é uma subclasse contínua de UIGestureRecognizer para reconhecer o gesto de pinça. Ao contrário dos reconhecedores discretos (UITapGestureRecognizer), ele gera uma sequência de eventos: .began (dedos tocam a tela), .changed (cada movimento dos dedos), .ended (dedos são levantados).

Propriedades principais: scale (CGFloat) — mudança relativa na distância entre os dedos desde o início do gesto; velocity (CGFloat) — taxa de mudança da escala por segundo. Escala inicial = 1.0. Ao afastar os dedos, a escala aumenta (1.0 → 1.5 → 2.0); ao juntá-los, diminui (1.0 → 0.8 → 0.5). A propriedade velocity é usada para inércia: após .ended, você pode animar a continuação da escala com desaceleração.

UIPinchGestureRecognizer suporta a propriedade delegate para reconhecimento simultâneo com outros reconhecedores (por exemplo, pan + pinch em um mapa). O método gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) retorna true, e ambos os gestos funcionam em paralelo. A Apple recomenda adicionar UIPinchGestureRecognizer à view contêiner em vez de elementos individuais — isso simplifica o cálculo do ponto pivô da escala.

Pinch-to-Zoom no Android: ScaleGestureDetector

ScaleGestureDetector é uma classe da Android Support Library (android.view) projetada para reconhecer escala multitoque. Ao contrário do GestureDetector.onFling, o ScaleGestureDetector é especificamente construído para zoom com dois dedos e calcula automaticamente focusX, focusY (o ponto central entre os dedos) e scaleFactor (delta de escala do evento anterior).

ScaleGestureDetector funciona através da interface OnScaleGestureListener com três métodos: onScaleBegin(detector) (análogo a .began), onScale(detector) (análogo a .changed, retorna boolean — true se a escala for aceita), onScaleEnd(detector) (análogo a .ended). Propriedades: scaleFactor — multiplicador de escala relativo ao evento anterior (~1.01 para afastamento lento), focusX/focusY — coordenadas do ponto central entre os dedos.

Para aplicar escala a uma View, geralmente se usa Matrix: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix suporta acúmulo de transformações — cada chamada postScale adiciona uma nova transformação à existente. É importante lembrar: ScaleGestureDetector não limita a escala mínima/máxima — essa é responsabilidade do desenvolvedor. Na IT Sectr, usamos ScaleGestureDetector com limites de escala personalizados (0.5–3.0) e animação de retorno ao exceder os limites.

Limitação do zoom e ponto pivô

Ao implementar Pinch-to-Zoom, é criticamente importante limitar a escala mínima e máxima — sem isso, o usuário pode ampliar até o infinito ou reduzir até a invisibilidade. Valores padrão: min = 0.5 (50% do tamanho original), max = 3.0 (300%). No iOS, a limitação é feita via clamp: newScale = min(max(currentScale * gesture.scale, minScale), maxScale). No Android — através de uma verificação após postScale.

O ponto pivô é o centro entre os dedos, em relação ao qual a escala é realizada. O iOS calcula automaticamente o centróide dos dois toques e o passa através de location(in:) — o desenvolvedor não precisa calcular o pivô manualmente. O Android fornece focusX/focusY no ScaleGestureDetector, mas ao aplicar via Matrix, o pivô é passado como terceiro e quarto argumentos em postScale(scaleFactor, scaleFactor, focusX, focusY). Sem um ponto pivô, a escala será realizada em relação ao canto superior esquerdo (0,0), o que faz o conteúdo se deslocar durante o zoom.

Com base na experiência da IT Sectr, o cálculo correto do ponto pivô é a fonte mais comum de bugs ao implementar Pinch-to-Zoom. Em 90% dos casos, usar as coordenadas integradas do GestureRecognizer e ScaleGestureDetector é suficiente — não é necessário calcular o pivô manualmente. Se o conteúdo se deslocar durante a escala, verifique se você está passando focusX/focusY para matrix.postScale().

Inércia após o zoom

Para uma UX suave após o término do gesto, você pode adicionar inércia: no iOS — use velocity do UIPinchGestureRecognizer com UIView.animate e parâmetros spring; no Android — OverScroller ou ValueAnimator. A inércia não deve ultrapassar os limites de escala mín/máx — o valor final é limitado com uma animação de retorno spring ao exceder o limite.

Exemplos de código em Swift e Kotlin

Exemplo 1: Pinch-to-Zoom no iOS (Swift)

Adiciona UIPinchGestureRecognizer a UIImageView com limitação de escala de 0.5 a 3.0. Mostra uma implementação mínima com preservação da escala acumulada.

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
        }
    }
}

Lógica: em .began/.changed, a escala é aplicada via CGAffineTransform com clamp. Em .ended, a escala é redefinida para 1.0 e o valor acumulado é armazenado em currentScale. Isso evita a deriva da escala durante gestos repetidos — cada novo gesto começa do estado atual. Para imagens, recomenda-se UIScrollView com minimumZoomScale/maximumZoomScale em vez de CGAffineTransform.

Exemplo 2: Pinch-to-Zoom no Android (Kotlin)

Implementa a escala de ImageView via ScaleGestureDetector com Matrix. Mostra o manuseio correto do ponto pivô e a limitação da escala.

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
    }
}

O ImageView personalizado sobrescreve onTouchEvent, passando eventos para ScaleGestureDetector. OnScaleListener.onScale() é chamado em cada movimento dos dedos. detector.scaleFactor é o delta (1.01 para afastamento lento). currentScale acumula com coerceIn para clamp. focusX/focusY — o ponto central entre os dedos, passado para postScale para escala correta do pivô.

Exemplo 3: UIScrollView com suporte a zoom (iOS)

UIScrollView possui suporte integrado a Pinch-to-Zoom através de seu delegate. Esta é a abordagem preferida para imagens e documentos — o ScrollView gerencia escala, inércia e rolagem automaticamente.

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 cria automaticamente um UIPinchGestureRecognizer, gerencia a escala através da transformação de CALayer e adiciona um efeito de ressalto ao ultrapassar os limites. O código se reduz a definir min/maxZoomScale e retornar viewForZooming. Na IT Sectr, usamos UIScrollView para todas as imagens em tela cheia — isso reduz o volume de código em 5 vezes em comparação com a implementação manual via UIPinchGestureRecognizer.

Perguntas frequentes

Como o pinch difere de um beliscão no trackpad?

Em uma tela sensível ao toque, pinch usa dois dedos com rastreamento da distância entre eles. Em um trackpad de MacBook, o zoom é implementado através de UIPinchGestureRecognizer com os mesmos estados (.began → .changed → .ended), mas a fonte do toque é um trackpad com Force Touch, não uma tela capacitiva. A API é a mesma, a mecânica é idêntica.

Como limitar o zoom mínimo e máximo?

No iOS: após obter a escala de UIPinchGestureRecognizer, multiplique por currentScale e aplique clamp(min: 0.5, max: 3.0). No Android: use coerceIn(minScale, maxScale) antes de matrix.postScale(). No UIScrollView — através das propriedades minimumZoomScale e maximumZoomScale. Sem limitação, a escala pode ir ao infinito ou zero.

Pinch-to-Zoom funciona com um dedo?

Não, o pinch requer pelo menos dois dedos. Para zoom com um dedo (duplo toque + arrastar para cima/baixo), é necessária uma implementação personalizada via UIPanGestureRecognizer ou GestureDetector.onScroll. Este é um gesto diferente com lógica diferente — geralmente usado para zoom na câmera ou mapas.

Resumo

  • Pinch-to-Zoom é um gesto multitoque com dois dedos para escalar conteúdo, o padrão para imagens, mapas e documentos.
  • UIPinchGestureRecognizer no iOS é um reconhecedor contínuo com scale (delta) e velocity (velocidade).
  • ScaleGestureDetector no Android é um detector especializado com onScale(), focusX/focusY para o ponto pivô.
  • Para UIScrollView, o zoom é integrado via minimumZoomScale/maximumZoomScale — mais simples que a implementação manual.
  • Sempre limite a escala (0.5–3.0) via clamp — sem isso, o conteúdo pode se tornar invisível.
  • O ponto pivô da escala é o centro entre os dedos; sem ele, o conteúdo se desloca durante o zoom.
  • Na IT Sectr, usamos UIScrollView para imagens e ScaleGestureDetector personalizado para editores — a escolha depende da complexidade das transformações.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também