Responder Chain es un mecanismo de iOS que pasa secuencialmente eventos de toque, pulsaciones de teclas y gestos a través de la jerarquía de objetos UIResponder hasta que uno de ellos maneja el evento. La cadena comienza con el objeto que detectó el evento y asciende por la jerarquía: desde la vista a su superview, luego al view controller, window y, finalmente, a UIApplication. Según la Apple Developer Documentation (2026), este patrón permite separar la responsabilidad del manejo de eventos entre los componentes de la interfaz, proporcionando flexibilidad sin una vinculación rígida a un manejador específico.
Puntos clave
Responder Chain es una secuencia de objetos UIResponder a través de los cuales iOS pasa eventos de entrada, como toques, pulsaciones de teclas y datos del acelerómetro. Cada objeto en esta cadena tiene la capacidad de manejar el evento o pasarlo al siguiente objeto respondedor mediante la propiedad next.
El mecanismo se basa en la jerarquía de vistas: cuando ocurre un toque, iOS primero determina qué vista fue tocada (mediante hit-testing) y crea una cadena comenzando desde esa vista hasta UIApplication. UIApplication es el último eslabón de la cadena: si un evento llega a él y no se maneja, simplemente se descarta.
Según Apple, este patrón es fundamental para encapsular la lógica de manejo de eventos. Un desarrollador puede sobrescribir el comportamiento de una vista específica sin afectar a otros elementos de la jerarquía. Por ejemplo, UITextField se convierte en first responder al recibir el foco y recibe eventos del teclado sin requerir cambios en el UIViewController padre.
El manejo de eventos a través de Responder Chain ocurre en dos etapas: primero, iOS determina qué vista recibió el evento (hit-testing), luego ejecuta la cadena de respondedores para manejarlo. Si un objeto no implementa el método correspondiente, el evento se pasa más adelante.
La cadena se forma dinámicamente basándose en el first responder actual y la jerarquía de vistas. El orden estándar es: first responder → su vista → superview → UIViewController → root view → UIWindow → UIApplication. Si alguno de estos objetos implementa, por ejemplo, touchesBegan, el evento se maneja en ese nivel y no se pasa más adelante.
import UIKit
class CustomView: UIView {
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
// Toque manejado en este nivel de vista
print("CustomView manejó el toque")
// Reenviar evento a lo largo de la cadena
super.touchesBegan(touches, with: event)
}
}
Una característica clave es que llamar a super.touchesBegan no es obligatorio. Si no se llama, el evento se manejará solo en el nivel actual y no continuará por la Responder Chain. Esto le da al desarrollador control total sobre qué objetos participan en el manejo.
Cuando un objeto recibe un evento pero no lo maneja (no sobrescribe el método), iOS automáticamente pasa el evento al siguiente objeto respondedor mediante la propiedad next. Esta propiedad forma una lista enlazada simple, que se llama Responder Chain.
UIViewController se encuentra entre su vista y UIWindow: si la vista no maneja el evento, el controlador tiene la oportunidad de hacerlo. Esto es especialmente útil para lógica común — por ejemplo, manejar un gesto que debe funcionar en toda la escena, independientemente de qué vista esté bajo el dedo del usuario.
Hit-Testing es el proceso mediante el cual iOS determina qué vista está debajo del punto de toque. El método hitTest:withEvent: recorre la jerarquía de vistas desde UIWindow hacia abajo, verificando qué vista hija contiene el punto de toque y no está oculta.
El algoritmo funciona recursivamente: para cada nivel, iOS verifica las vistas en orden inverso de agregado (las superiores primero). Si una vista no está oculta, no es transparente y el punto cae dentro de sus bounds, se ejecuta hitTest recursivamente para todas sus subviews. La vista más profunda que cumple todas las condiciones se convierte en la hit-test view — el primer objeto en la Responder Chain.
override func hitTest(
_ point: CGPoint,
with event: UIEvent?
) -> UIView? {
if isUserInteractionEnabled &&
isHidden == false &&
alpha > 0.01 &&
point(inside: point, with: event) {
return super.hitTest(point, with: event)
}
return nil
}
Los desarrolladores pueden sobrescribir hitTest para cambiar el comportamiento predeterminado. Por ejemplo, para extender el área de toque de un botón pequeño o redirigir el evento a otra vista que no está físicamente bajo el dedo. Esta es una herramienta poderosa para crear elementos interactivos personalizados.
UIResponder es la clase base para todos los objetos que pueden manejar eventos en iOS. UIView, UIViewController, UIApplication y UIWindow heredan de UIResponder. La clase proporciona un conjunto de métodos que se pueden sobrescribir para manejar diferentes tipos de eventos.
Los grupos principales de métodos incluyen touchesBegan, touchesMoved, touchesEnded, touchesCancelled para toques; pressesBegan, pressesEnded para botones físicos; y motionBegan, motionEnded para eventos del acelerómetro. Cada método recibe un conjunto de objetos UITouch o UIPress y una referencia a UIEvent que contiene metadatos adicionales sobre el evento.
| Método UIResponder | Propósito |
|---|---|
| touchesBegan | Se llama cuando comienza un toque |
| touchesMoved | Se llama cuando un dedo se mueve |
| touchesEnded | Se llama cuando se levanta un dedo |
| touchesCancelled | Se llama en interrupción (llamada, deslizamiento de panel) |
| pressesBegan | Se llama cuando se presiona un botón físico |
Es importante entender que iOS llama a estos métodos solo para el first responder y los objetos subsiguientes en la cadena. Si ningún objeto ha sobrescrito el método, el evento no genera un error — simplemente se ignora. Para depurar el manejo de eventos, use un Symbolic Breakpoint en UIResponder touchEvent.
Los componentes UIKit estándar utilizan activamente la Responder Chain para su funcionamiento. UITextField se convierte en first responder cuando recibe el foco, lo que abre automáticamente el teclado. UIButton maneja toques a través del mecanismo UIControl, que también se basa en la responder chain.
UITableView y UICollectionView usan la cadena de respondedores para manejar la selección de celdas y los gestos de desplazamiento. Si un usuario toca una celda, el evento primero llega a la celda misma, luego a UITableView, y solo después a UIViewController. UIGestureRecognizer tiene mayor prioridad que touchesBegan — si se agrega un reconocedor de gestos a una vista, recibirá el evento primero.
Según Apple, el uso correcto de Responder Chain es fundamental para la accesibilidad de la aplicación. VoiceOver y otras tecnologías asistivas utilizan la cadena de respondedores para navegar entre elementos de la interfaz. Si la cadena está rota, los usuarios con discapacidades no podrán interactuar con la aplicación.
UIMenuController para mostrar menús contextuales también usa la Responder Chain. Cuando un usuario invoca el menú, el sistema busca un first responder que implemente los métodos canPerformAction y los métodos action correspondientes. El menú se muestra solo para las acciones que soporta el respondedor actual.
Esto permite, por ejemplo, mostrar los comandos Cortar, Copiar, Pegar solo cuando UITextField está en foco, y ocultarlos al trabajar con UILabel. Un desarrollador puede agregar acciones personalizadas al menú contextual implementándolas en una subclase de UIResponder y devolviendo true desde canPerformAction.
Crear un objeto respondedor personalizado le da al desarrollador control total sobre el manejo de eventos. Para hacerlo, necesita crear una subclase de UIResponder (o UIView/UIViewController) y sobrescribir los métodos necesarios de manejo de eventos.
Los objetos respondedor personalizados se utilizan a menudo para manejar gestos específicos que no están cubiertos por UIGestureRecognizer estándar. Por ejemplo, reconocer dibujo de formas, combinaciones complejas multi-touch o patrones de entrada propietarios. Un respondedor personalizado puede agregar eventos de varios dedos y tomar decisiones basadas en su combinación.
class DrawingResponder: UIResponder {
private var activeTouches: [UITouch: CGPoint] = [:]
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
for touch in touches {
activeTouches[touch] = touch.location(in: self)
}
}
override func touchesMoved(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
for touch in touches {
let currentPoint = touch.location(in: self)
activeTouches[touch] = currentPoint
drawLine(from: activeTouches[touch]!, to: currentPoint)
}
}
}
Al crear un respondedor personalizado, es importante configurar correctamente la cadena next. Si su objeto no forma parte de la jerarquía estándar de UIKit, debe especificar explícitamente qué objeto será su next responder. Esto garantiza que los eventos no manejados continúen moviéndose a lo largo de la Responder Chain.
Preguntas frecuentes
Responder Chain es una cadena jerárquica de objetos UIResponder a través de la cual iOS pasa secuencialmente eventos de toque, pulsaciones y gestos. Si un objeto no maneja el evento, se pasa al siguiente objeto respondedor en la cadena hasta UIApplication.
Puede cambiar el orden sobrescribiendo la propiedad next de su objeto UIResponder. Al devolver un objeto diferente en lugar del predeterminado, redirige los eventos no manejados hacia él. Esto es útil para jerarquías no estándar, por ejemplo, cuando un contenedor personalizado gestiona múltiples controladores hijos.
Hit-Testing determina qué vista está debajo del punto de toque (el receptor inicial), mientras que Responder Chain determina cómo se pasa el evento entre objetos después del hit-test. Hit-test encuentra el primer objeto, la responder chain proporciona el enrutamiento adicional si ese objeto no maneja el evento.
Para interrumpir la cadena, simplemente maneje el evento en su UIResponder y no llame a super. Por ejemplo, sobrescribiendo touchesBegan y no llamando a super.touchesBegan, evita que el evento se pase más adelante. El evento se manejará en el nivel actual y no alcanzará los siguientes eslabones de la cadena.
Las razones más comunes: isUserInteractionEnabled está establecido en false, la vista está oculta (isHidden = true), alpha es menor a 0.01, o la vista está fuera de los bounds del contenedor padre. También verifique que no haya un UIGestureRecognizer en la vista o su superview que intercepte los eventos antes de touchesBegan.
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