Principais pontos
UIGestureRecognizer é uma classe base abstrata no UIKit que decompõe o processo de reconhecimento de gestos em objetos separados. Cada subclass lida com um tipo de gesto: UITapGestureRecognizer lida com um tap com um número específico de toques, UISwipeGestureRecognizer lida com um swipe em uma direção determinada. O desenvolvedor adiciona o reconhecedor ao UIView através do método addGestureRecognizer(:), e o UIKit rastreia automaticamente os toques, atualiza o estado e chama uma ação ao reconhecer.
Antes do UIGestureRecognizer (iOS 3.2, 2010), os desenvolvedores sobrescreviam métodos do UIResponder — touchesBegan, touchesMoved, touchesEnded — e analisavam manualmente as trajetórias dos toques. Isso causava duplicação de código e erros ao lidar com toques simultâneos. A Apple encapsulou essa lógica no UIGestureRecognizer, adicionando suporte para multitoque, cancelamento de gestos e operação simultânea de vários reconhecedores em uma mesma view.
De acordo com as sessões da WWDC, o Gesture Recognizer lida com até 11 toques simultâneos no iPad e 5 no iPhone. Na IT Sectr, usamos UIGestureRecognizer como a forma padrão de lidar com entrada do usuário em todos os projetos UIKit — isso eliminou bugs associados ao rastreio manual de touchesBegan.
Cada UIGestureRecognizer passa por 7 estados possíveis, definidos no enum UIGestureRecognizer.State. Esses estados refletem o ciclo de vida do reconhecimento: desde a deteção do toque até a conclusão ou cancelamento. Compreender os estados é crítico para implementar reconhecedores personalizados e depurar conflitos.
| Estado | Significado | Quando ocorre |
|---|---|---|
| .possible | Estado inicial, gesto ainda não reconhecido | Imediatamente após ser adicionado a uma view |
| .began | Gesto reconhecido e começou a executar | No primeiro movimento do dedo para pan/longPress |
| .changed | Parâmetros do gesto mudaram (coordenadas, ângulo) | Em cada movimento do dedo |
| .ended | Utilizador levantou o dedo, gesto concluído | No touchesEnded |
| .cancelled | Gesto interrompido pelo sistema (chamada recebida, mudança de orientação) | No touchesCancelled |
| .failed | Gesto não reconhecido conforme as condições | No touchesCancelled sem reconhecimento |
| .recognized | Sinónimo de .ended; gesto reconhecido com sucesso | Igual ao .ended |
Gestos discretos (tap, swipe) passam de .possible diretamente para .ended ou .failed. Gestos contínuos (pan, pinch, rotation, longPress) percorrem .possible → .began → .changed (repetidamente) → .ended. No método de ação, verifique gestureRecognizer.state — isto permite distinguir o início, a mudança e o fim de um gesto.
UIKit fornece 7 subclasses integradas de UIGestureRecognizer, cobrindo a maioria dos cenários de interação. Cada subclass tem configurações específicas: numberOfTapsRequired para tap, direction para swipe, minimumPressDuration para pressão longa.
Para gestos personalizados (por exemplo, desenhar um ziguezague), cria-se uma subclass de UIGestureRecognizer sobrescrevendo touchesBegan, touchesMoved, touchesEnded e atualizando o estado. A Apple recomenda usar as classes integradas sempre que possível — elas são otimizadas e interagem corretamente entre si.
Quando vários UIGestureRecognizer são usados numa mesma UIView (por exemplo, tap e duplo tap), ocorre um conflito de reconhecimento: num duplo tap, o tap simples dispara primeiro. A Apple fornece o método require(toFail:) para atrasar o reconhecimento de um gesto até que outro falhe.
O mecanismo funciona assim: ao chamar tapRecognizer.require(toFail: doubleTapRecognizer), especifica-se que tapRecognizer transitará para o estado .recognized apenas depois de doubleTapRecognizer terminar em .failed. Isto adiciona um atraso de ~0,3 segundos antes de executar o tap simples — o utilizador toca duas vezes e o primeiro tap é ignorado. Uma abordagem alternativa é o delegado UIGestureRecognizerDelegate com o método gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), que permite reconhecimento simultâneo (por exemplo, pan + pinch para um mapa).
De acordo com a WWDC 2020, cerca de 15% dos erros em aplicações UIKit estão relacionados com configuração incorreta de conflitos de gestos. Na IT Sectr, padronizámos a abordagem: cada ecrã tem um esquema de gestos com prioridades require(toFail:) — isto eliminou completamente os erros de dupla ativação de taps.
Adiciona um manipulador de tap simples ao UIImageView. Ao tocar, a imagem muda de opacidade — um exemplo simples que mostra a vinculação de um gesto a uma view.
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
}
}
}
Ponto chave: isUserInteractionEnabled no UIImageView é false por defeito — sem esta flag, o Gesture Recognizer não receberá toques. Para UIView e UIButton, esta flag está ativada por defeito. O método de ação aceita um parâmetro UITapGestureRecognizer, através do qual se pode obter location(in:) para determinar as coordenadas do tap.
Implementa um deslize para a esquerda para voltar ao ecrã anterior. Demonstra a configuração de direção e a vinculação do gesto à view raiz do controlador.
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 é um gesto discreto: transita para .recognized imediatamente após o reconhecimento, sem estados .changed intermédios. Portanto, não é necessário verificar o estado na ação — o gesto ou é reconhecido (a ação dispara) ou não. A propriedade direction aceita um de quatro valores: .left, .right, .up, .down. Para suportar múltiplas direções, crie reconhecedores separados para cada uma.
Mostra como configurar tap simples e duplo numa mesma view sem conflito. O reconhecedor de duplo tap tem prioridade — o tap simples dispara apenas se o duplo tap não for reconhecido.
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")
}
}
Sem require(toFail:), num duplo tap, handleSingleTap dispara primeiro, depois handleDoubleTap — isto quebra a UX. Com require(toFail:), o tap simples espera ~0,3 segundos para garantir que um segundo tap não se segue. Na IT Sectr, este padrão é usado em editores de imagem e galerias, onde um duplo tap faz zoom e um tap simples seleciona um elemento.
Perguntas frequentes
Sim, UIView suporta várias instâncias de UIGestureRecognizer simultaneamente. Para resolver conflitos, use o método require(toFail:) que define a ordem de reconhecimento. Para operação paralela de gestos (por exemplo, pan + pinch num mapa), implemente o método delegado gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) retornando true.
UIGestureRecognizer é uma abstração de alto nível que reconhece automaticamente padrões de toque e gere estados. touchesBegan é um método de baixo nível do UIResponder que requer rastreio manual de coordenadas, temporização e cancelamento de toques. Gesture Recognizer é mais simples, fiável e preferível para gestos padrão; touchesBegan só se justifica para gráficos personalizados.
O SwiftUI nativo usa modificadores: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Estes são análogos declarativos do UIGestureRecognizer integrados na hierarquia do SwiftUI. Se necessário, pode-se envolver um reconhecedor UIKit através de UIViewRepresentable, mas a Apple recomenda usar gestos nativos do SwiftUI.
Resumo
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.
Leia também