Puntos clave
UIGestureRecognizer es una clase base abstracta en UIKit que descompone el proceso de reconocimiento de gestos en objetos separados. Cada subclase maneja un tipo de gesto: UITapGestureRecognizer maneja un tap con un número específico de toques, UISwipeGestureRecognizer maneja un swipe en una dirección determinada. El desarrollador añade el reconocedor a UIView mediante el método addGestureRecognizer(:), y UIKit rastrea automáticamente los toques, actualiza el estado y llama a una acción al reconocerlo.
Antes de UIGestureRecognizer (iOS 3.2, 2010), los desarrolladores sobrescribían los métodos de UIResponder — touchesBegan, touchesMoved, touchesEnded — y analizaban manualmente las trayectorias de los toques. Esto provocaba duplicación de código y errores al manejar toques simultáneos. Apple encapsuló esta lógica en UIGestureRecognizer, añadiendo soporte para multitáctil, cancelación de gestos y operación simultánea de múltiples reconocedores en una misma vista.
Según las sesiones de WWDC, Gesture Recognizer maneja hasta 11 toques simultáneos en iPad y 5 en iPhone. En IT Sectr, usamos UIGestureRecognizer como la forma estándar de manejar la entrada del usuario en todos los proyectos UIKit — esto eliminó los errores asociados con el rastreo manual de touchesBegan.
Cada UIGestureRecognizer pasa por 7 estados posibles, definidos en el enum UIGestureRecognizer.State. Estos estados reflejan el ciclo de vida del reconocimiento: desde la detección del toque hasta la finalización o cancelación. Comprender los estados es crítico para implementar reconocedores personalizados y depurar conflictos.
| Estado | Significado | Cuándo ocurre |
|---|---|---|
| .possible | Estado inicial, gesto aún no reconocido | Inmediatamente después de añadirse a una vista |
| .began | Gesto reconocido y comenzó a ejecutarse | En el primer movimiento del dedo para pan/longPress |
| .changed | Los parámetros del gesto cambiaron (coordenadas, ángulo) | En cada movimiento del dedo |
| .ended | El usuario levantó el dedo, gesto completado | En touchesEnded |
| .cancelled | Gesto interrumpido por el sistema (llamada entrante, cambio de orientación) | En touchesCancelled |
| .failed | Gesto no reconocido según las condiciones | En touchesCancelled sin reconocimiento |
| .recognized | Sinónimo de .ended; gesto reconocido exitosamente | Igual que .ended |
Los gestos discretos (tap, swipe) pasan de .possible directamente a .ended o .failed. Los gestos continuos (pan, pinch, rotation, longPress) recorren .possible → .began → .changed (repetidamente) → .ended. En el método de acción, verifica gestureRecognizer.state — esto permite distinguir el inicio, el cambio y el final de un gesto.
UIKit proporciona 7 subclases incorporadas de UIGestureRecognizer, que cubren la mayoría de los escenarios de interacción. Cada subclase tiene configuraciones específicas: numberOfTapsRequired para tap, direction para swipe, minimumPressDuration para pulsación larga.
Para gestos personalizados (por ejemplo, dibujar un zigzag), se crea una subclase de UIGestureRecognizer sobrescribiendo touchesBegan, touchesMoved, touchesEnded y actualizando el estado. Apple recomienda usar las clases incorporadas cuando sea posible — están optimizadas e interactúan correctamente entre sí.
Cuando se usan múltiples UIGestureRecognizer en una misma UIView (por ejemplo, tap y doble tap), ocurre un conflicto de reconocimiento: en un doble tap, el tap simple se dispara primero. Apple proporciona el método require(toFail:) para retrasar el reconocimiento de un gesto hasta que otro falle.
El mecanismo funciona así: al llamar tapRecognizer.require(toFail: doubleTapRecognizer), se especifica que tapRecognizer pasará al estado .recognized solo después de que doubleTapRecognizer termine en .failed. Esto añade un retraso de ~0.3 segundos antes de ejecutar el tap simple — el usuario toca dos veces y el primer tap se ignora. Un enfoque alternativo es el delegado UIGestureRecognizerDelegate con el método gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), que permite el reconocimiento simultáneo (por ejemplo, pan + pinch para un mapa).
Según WWDC 2020, alrededor del 15% de los errores en aplicaciones UIKit están relacionados con una configuración incorrecta de conflictos de gestos. En IT Sectr, estandarizamos el enfoque: cada pantalla tiene un esquema de gestos con prioridades require(toFail:) — esto eliminó por completo los errores de doble activación de taps.
Añade un manejador de tap simple a UIImageView. Al tocar, la imagen cambia de opacidad — un ejemplo sencillo que muestra la vinculación de un gesto a una vista.
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
}
}
}
Punto clave: isUserInteractionEnabled en UIImageView es false por defecto — sin esta bandera, Gesture Recognizer no recibirá toques. Para UIView y UIButton, esta bandera está activada por defecto. El método de acción acepta un parámetro UITapGestureRecognizer, a través del cual se puede obtener location(in:) para determinar las coordenadas del tap.
Implementa un deslizamiento a la izquierda para volver a la pantalla anterior. Demuestra la configuración de dirección y la vinculación del gesto a la vista raíz del 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 es un gesto discreto: pasa a .recognized inmediatamente después del reconocimiento, sin estados .changed intermedios. Por lo tanto, no es necesario verificar el estado en la acción — el gesto se reconoce (la acción se dispara) o no. La propiedad direction acepta uno de cuatro valores: .left, .right, .up, .down. Para admitir múltiples direcciones, crea reconocedores separados para cada una.
Muestra cómo configurar tap simple y doble en una misma vista sin conflicto. El reconocedor de doble tap tiene prioridad — el tap simple se dispara solo si el doble tap no se reconoce.
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")
}
}
Sin require(toFail:), en un doble tap, handleSingleTap se dispara primero, luego handleDoubleTap — esto rompe la UX. Con require(toFail:), el tap simple espera ~0.3 segundos para asegurarse de que no sigue un segundo tap. En IT Sectr, este patrón se usa en editores de imágenes y galerías, donde un doble tap hace zoom y un tap simple selecciona un elemento.
Preguntas frecuentes
Sí, UIView admite múltiples instancias de UIGestureRecognizer simultáneamente. Para resolver conflictos, usa el método require(toFail:) que establece el orden de reconocimiento. Para la operación paralela de gestos (por ejemplo, pan + pinch en un mapa), implementa el método delegado gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) devolviendo true.
UIGestureRecognizer es una abstracción de alto nivel que reconoce automáticamente patrones de toque y gestiona estados. touchesBegan es un método de bajo nivel de UIResponder que requiere seguimiento manual de coordenadas, temporización y cancelación de toques. Gesture Recognizer es más simple, fiable y preferible para gestos estándar; touchesBegan solo se justifica para gráficos personalizados.
SwiftUI nativo utiliza modificadores: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Estos son análogos declarativos de UIGestureRecognizer integrados en la jerarquía de SwiftUI. Si es necesario, se puede envolver un reconocedor UIKit mediante UIViewRepresentable, pero Apple recomienda usar gestos nativos de SwiftUI.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también