Chaîne de répondeurs iOS ou ce qu'ils demandent lors de l'entretien

image


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 .


image


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

Source: https://habr.com/ru/post/fr464463/


All Articles