
Quelle est la différence entre le premier et le deuxième exemple?
De quoi la cible est-elle responsable?
Dans quel cas la méthode est-elle appelée lorsque le bouton est cliqué?
TL; DR
Lorsqu'un bouton est cliqué, notre méthode est appelée dans les deux cas.
Seulement dans le premier exemple, UIKit essaiera d'appeler la méthode dans la cible affectée (nous avons ViewController
). Il plantera si cette méthode n'existe pas.
Dans le deuxième exemple, la chaîne de UIResponder
iOS est utilisée, UIKit
recherchera le UIResponder
le plus UIResponder
-a qui a cette méthode. Il n'y aura pas de plantage si notre méthode n'est pas trouvée.
UIViewController, UIView, UIApplication
héritent de UIResponder
.
iOS Responder Chain et ce qui est sous le capot
L' UIKit
processus de chaîne de répondeur iOS est géré par UIKit
, qui fonctionne dynamiquement avec une liste UIResponder
de UIResponder
. Cette liste UIKit
créée à partir du premier répondeur (le premier UIResponder
qui a enregistré l'événement, nous avons UIButton(UIView)
et ses sous- subviews
.

UIKit passe en UIResponder
la liste des UIResponder
et vérifie avec canPerformAction
pour notre fonction.
open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool
Si le UIResponder
sélectionné UIResponder
peut pas fonctionner avec une méthode spécifique,
UIKit
envoie récursivement des actions au prochain UIResponder
de la liste en utilisant la méthode target
qui renvoie le prochain UIResponder
.
open func target(forAction action: Selector, withSender sender: Any?) -> Any?
Ce processus est répété jusqu'à ce que l'un des UIResponder
puisse fonctionner avec notre méthode ou que la liste se termine et que le système ignore cet événement.
Dans l'exemple du deuxième clic, il a été géré par l' UIViewController
, mais UIKit
abord envoyé une demande à l' UIView
puisqu'il était le premier répondant. Il ne disposait pas de la méthode requise, donc UIKit
redirigé les actions vers le prochain UIResponder
dans une liste chaînée qui était l' UIViewController
qui avait la méthode souhaitée.
Dans la plupart des cas, la iOS Responder Chain
est une simple liste subviews
de sous- subviews
, mais son ordre peut être modifié. Vous pouvez faire UIResponder (becomeFirstResponder)
devenir
premier UIResponder
et le remettre à son ancienne position en utilisant resignFirstResponder
. Ceci est souvent utilisé avec un UITextField
pour montrer le clavier qui sera appelé uniquement lorsque l' UITextField
est le first responder
.
Chaîne de répondeurs iOS et UIEvent
La chaîne répondeur est également impliquée dans le toucher de l'écran, les mouvements, les clics. Lorsque le système détecte une sorte d'événement (toucher, mouvement, télécommande, pression), un UIEvent
est créé sous le capot et envoyé à l'aide de la méthode UIApplication.shared.sendEvent()
à UIWindow
. Après avoir reçu l'événement, UIWindow
détermine à l'aide de la hitTest:withEvent
à quel UIResponder
cet événement appartient et lui attribue le first responder
. Vient ensuite le travail avec une liste UIResponder
de UIResponder
décrite ci-dessus.
Pour travailler avec les UIEvent
système, les sous-classes de UIResponder (UIViewController, UIView, UIApplication)
peuvent remplacer ces méthodes:
open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) open func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesChanged(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func motionCancelled(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func remoteControlReceived(with event: UIEvent?)
Malgré la possibilité d'hériter et d'appeler sendEvent
manuellement, UIResponder
pas prévu pour cela. Cela peut créer de nombreux problèmes avec le fonctionnement des événements personnalisés, ce qui peut conduire à des actions incompréhensibles causées par un first responeder
aléatoire qui peut répondre à votre événement.
Pourquoi est-il utile, où l'utiliser
Malgré le fait que la iOS Responder Chain
entièrement contrôlée par UIKit
, elle peut être utilisée pour résoudre le problème de délégation / communication. UIResponder
actions UIResponder
sont similaires à NotificationCenter.default.post
UIResponder
unique.
Prenons un exemple, nous avons un UIViewController
, qui est profondément dans la pile UINavigationController et nous devons lui dire ce qui s'est passé lorsqu'un bouton a été cliqué sur un autre écran. Vous pouvez utiliser le modèle de délagation ou NotificationCenter.default.post
, mais une option assez simple consiste à utiliser la iOS Responder Chain
réponse iOS Responder Chain
.
button.addTarget(nil, action: #selector(RootVC.doSomething), for: .touchUpInside)
Lorsque vous appuyez UIViewController
, la méthode à la UIViewController
sera appelée. #selector peut prendre les paramètres suivants:
@objc func doSomething() @objc func doSomething(sender: Any?) @objc func doSomething(sender: Any?, event: UIEvent?)
l'expéditeur est l'objet qui a envoyé l'événement - UIButton, UITextField, etc.
Ressources supplémentaires pour l'apprentissage [eng]:
Bonne description de UIEvent, UIResponder et quelques exemples avancés (coordinateur de patern)
En savoir plus sur la chaîne de répondeurs iOS
Exemple de chaîne de répondeurs en pratique
Off dock sur la chaîne de répondeur iOS
Off Dock par UIResponder