Responder Chain è un meccanismo iOS che trasmette sequenzialmente eventi di tocco, pressione dei tasti e gesti attraverso la gerarchia di oggetti UIResponder finché uno di essi non elabora l'evento. La catena inizia con l'oggetto che ha rilevato l'evento e risale la gerarchia: dalla view alla sua superview, poi al view controller, alla window e infine a UIApplication. Secondo la Apple Developer Documentation (2026), questo modello consente di separare la responsabilità della gestione degli eventi tra i componenti dell'interfaccia, offrendo flessibilità senza un legame rigido con un gestore specifico.
Punti chiave
Responder Chain è una sequenza di oggetti UIResponder attraverso la quale iOS trasmette eventi di input come tocchi, pressioni di tasti e dati dell'accelerometro. Ogni oggetto in questa catena ha la capacità di gestire l'evento o passarlo all'oggetto risponditore successivo tramite la proprietà next.
Il meccanismo si basa sulla gerarchia delle view: quando si verifica un tocco, iOS determina prima quale view è stata toccata (tramite hit-testing) e crea una catena che inizia da quella view fino a UIApplication. UIApplication è l'ultimo anello della catena — se un evento lo raggiunge e non viene gestito, viene semplicemente scartato.
Secondo Apple, questo modello è fondamentale per incapsulare la logica di gestione degli eventi. Uno sviluppatore può sovrascrivere il comportamento di una view specifica senza influenzare altri elementi nella gerarchia. Ad esempio, UITextField diventa first responder quando riceve il focus e riceve eventi della tastiera senza richiedere modifiche nel UIViewController padre.
La gestione degli eventi attraverso la Responder Chain avviene in due fasi: prima, iOS determina quale view ha ricevuto l'evento (hit-testing), poi esegue la catena di risponditori per gestirlo. Se un oggetto non implementa il metodo corrispondente, l'evento viene passato oltre.
La catena si forma dinamicamente in base al first responder corrente e alla gerarchia delle view. L'ordine standard è: first responder → la sua view → superview → UIViewController → root view → UIWindow → UIApplication. Se uno di questi oggetti implementa, ad esempio, touchesBegan, l'evento viene gestito a quel livello e non viene passato oltre.
import UIKit
class CustomView: UIView {
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
// Tocco gestito a questo livello di view
print("CustomView ha gestito il tocco")
// Inoltra evento lungo la catena
super.touchesBegan(touches, with: event)
}
}
Una caratteristica fondamentale è che chiamare super.touchesBegan non è obbligatorio. Se non viene chiamato, l'evento verrà gestito solo al livello corrente e non proseguirà lungo la Responder Chain. Questo dà allo sviluppatore il controllo completo su quali oggetti partecipano alla gestione.
Quando un oggetto riceve un evento ma non lo gestisce (non sovrascrive il metodo), iOS trasmette automaticamente l'evento all'oggetto risponditore successivo tramite la proprietà next. Questa proprietà forma una lista concatenata semplice, chiamata Responder Chain.
UIViewController si trova tra la sua view e UIWindow: se la view non gestisce l'evento, il controller ha l'opportunità di farlo. Questo è particolarmente utile per logica comune — ad esempio, gestire un gesto che dovrebbe funzionare sull'intera scena, indipendentemente da quale view si trova sotto il dito dell'utente.
Hit-Testing è il processo con cui iOS determina quale view si trova sotto il punto di tocco. Il metodo hitTest:withEvent: attraversa la gerarchia delle view da UIWindow verso il basso, verificando quale view figlia contiene il punto di tocco e non è nascosta.
L'algoritmo funziona ricorsivamente: per ogni livello, iOS controlla le view in ordine inverso di aggiunta (prima quelle superiori). Se una view non è nascosta, non è trasparente e il punto cade all'interno dei suoi bounds, hitTest viene eseguito ricorsivamente per tutte le sue subview. La view più profonda che soddisfa tutte le condizioni diventa la hit-test view — il primo oggetto nella 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
}
Gli sviluppatori possono sovrascrivere hitTest per modificare il comportamento predefinito. Ad esempio, per estendere l'area di tocco di un piccolo pulsante o reindirizzare l'evento a un'altra view che non si trova fisicamente sotto il dito. Questo è un potente strumento per creare elementi interattivi personalizzati.
UIResponder è la classe base per tutti gli oggetti che possono gestire eventi in iOS. UIView, UIViewController, UIApplication e UIWindow ereditano da UIResponder. La classe fornisce un insieme di metodi che possono essere sovrascritti per gestire diversi tipi di eventi.
I principali gruppi di metodi includono touchesBegan, touchesMoved, touchesEnded, touchesCancelled per i tocchi; pressesBegan, pressesEnded per i pulsanti fisici; e motionBegan, motionEnded per gli eventi dell'accelerometro. Ogni metodo riceve un insieme di oggetti UITouch o UIPress e un riferimento a UIEvent contenente metadati aggiuntivi sull'evento.
| Metodo UIResponder | Scopo |
|---|---|
| touchesBegan | Chiamato all'inizio di un tocco |
| touchesMoved | Chiamato quando un dito si muove |
| touchesEnded | Chiamato quando un dito viene sollevato |
| touchesCancelled | Chiamato in caso di interruzione (chiamata, scorrimento centro di controllo) |
| pressesBegan | Chiamato quando si preme un pulsante fisico |
È importante capire che iOS chiama questi metodi solo per il first responder e gli oggetti successivi nella catena. Se nessun oggetto ha sovrascritto il metodo, l'evento non genera un errore — viene semplicemente ignorato. Per eseguire il debug della gestione degli eventi, utilizzare un Symbolic Breakpoint su UIResponder touchEvent.
I componenti UIKit standard utilizzano attivamente la Responder Chain per il loro funzionamento. UITextField diventa first responder quando riceve il focus, aprendo automaticamente la tastiera. UIButton gestisce i tocchi attraverso il meccanismo UIControl, che si basa anch'esso sulla catena di risponditori.
UITableView e UICollectionView usano la catena di risponditori per gestire la selezione delle celle e i gesti di scorrimento. Se un utente tocca una cella, l'evento raggiunge prima la cella stessa, poi UITableView, e solo successivamente UIViewController. UIGestureRecognizer ha priorità più alta di touchesBegan — se a una view viene aggiunto un riconoscitore di gesti, riceverà l'evento per primo.
Secondo Apple, l'uso corretto della Responder Chain è fondamentale per l'accessibilità dell'app. VoiceOver e altre tecnologie assistive utilizzano la catena di risponditori per navigare tra gli elementi dell'interfaccia. Se la catena è interrotta, gli utenti con disabilità non potranno interagire con l'applicazione.
UIMenuController per la visualizzazione dei menu contestuali utilizza anch'esso la Responder Chain. Quando un utente invoca il menu, il sistema cerca un first responder che implementi i metodi canPerformAction e i corrispondenti metodi di azione. Il menu viene visualizzato solo per le azioni che il risponditore corrente supporta.
Ciò consente, ad esempio, di mostrare i comandi Taglia, Copia, Incolla solo quando UITextField è in focus e di nasconderli quando si lavora con UILabel. Uno sviluppatore può aggiungere azioni personalizzate al menu contestuale implementandole in una sottoclasse di UIResponder e restituendo true da canPerformAction.
Creare un oggetto risponditore personalizzato dà allo sviluppatore il controllo completo sulla gestione degli eventi. Per farlo, è necessario creare una sottoclasse di UIResponder (o UIView/UIViewController) e sovrascrivere i metodi di gestione degli eventi necessari.
Gli oggetti risponditore personalizzati sono spesso utilizzati per gestire gesti specifici che non sono coperti da UIGestureRecognizer standard. Ad esempio, riconoscimento di disegno di forme, combinazioni multi-touch complesse o modelli di input proprietari. Un risponditore personalizzato può aggregare eventi da più dita e prendere decisioni in base alla loro combinazione.
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)
}
}
}
Quando si crea un risponditore personalizzato, è importante configurare correttamente la catena next. Se il tuo oggetto non fa parte della gerarchia standard di UIKit, devi specificare esplicitamente quale oggetto sarà il suo next responder. Ciò garantisce che gli eventi non gestiti continuino a muoversi lungo la Responder Chain.
Domande frequenti
Responder Chain è una catena gerarchica di oggetti UIResponder attraverso la quale iOS trasmette sequenzialmente eventi di tocco, pressione e gesti. Se un oggetto non gestisce l'evento, viene passato all'oggetto risponditore successivo nella catena fino a UIApplication.
Puoi modificare l'ordine sovrascrivendo la proprietà next del tuo oggetto UIResponder. Restituendo un oggetto diverso invece di quello predefinito, reindirizzi gli eventi non gestiti ad esso. Ciò è utile per gerarchie non standard, ad esempio quando un contenitore personalizzato gestisce più controller figli.
Hit-Testing determina quale view si trova sotto il punto di tocco (il destinatario iniziale), mentre Responder Chain determina come l'evento viene trasmesso tra gli oggetti dopo l'hit-test. Hit-test trova il primo oggetto, la responder chain fornisce il routing aggiuntivo se quell'oggetto non gestisce l'evento.
Per interrompere la catena, gestisci semplicemente l'evento nel tuo UIResponder e non chiamare super. Ad esempio, sovrascrivendo touchesBegan e non chiamando super.touchesBegan, impedisci che l'evento venga passato oltre. L'evento verrà gestito al livello corrente e non raggiungerà gli anelli successivi della catena.
Le ragioni più comuni: isUserInteractionEnabled è impostato su false, la view è nascosta (isHidden = true), alpha è inferiore a 0,01, o la view è al di fuori dei bounds del contenitore padre. Verifica anche che non ci sia un UIGestureRecognizer sulla view o sulla sua superview che intercetti gli eventi prima di touchesBegan.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche