iOS Responder Chain or What they ask at the interview

image


What is the difference between the first and second example?

What is the target responsible for?

In which case is the method called when the button is clicked?

TL; DR


When a button is clicked, our method is called in both cases.


Only in the first example, UIKit will try to call the method in the assigned target (for us this is ViewController ). It will crash if this method does not exist.


In the second example, the iOS Responder Chain is used, UIKit will look for the nearest UIResponder -a which has this method. There will be no crash if our method is not found.


UIViewController, UIView, UIApplication inherit from UIResponder .


iOS Responder Chain and what's under the hood


The UIKit iOS Responder Chain process is handled by UIKit , which dynamically works with a linked list of UIResponder . This UIKit list UIKit created from the first responder (the first UIResponder that registered the event, we have UIButton(UIView) and its subviews .


image


UIKit goes through the list of UIResponder and checks with canPerformAction for our function.


 open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool 

If the selected UIResponder cannot work with a specific method,
UIKit recursively sends actions to the next UIResponder in the list using the target method which returns the next UIResponder .


 open func target(forAction action: Selector, withSender sender: Any?) -> Any? 

This process is repeated until one of the UIResponder can work with our method or the array ends and the system ignores this event.


In the second example, clicks were processed by the UIViewController , but UIKit first sent a request to the UIView since it was the first responder. He did not have the required method, so UIKit redirected actions to the next UIResponder in a linked list who was the UIViewController that had the desired method.


In most cases, the iOS Responder Chain is a simple array of subviews , but its order can be changed. You can make UIResponder (becomeFirstResponder) become
first UIResponder and return it to the old position using resignFirstResponder . This is often used with a UITextField to show the keyboard that will be called only when the UITextField is the first responder .


iOS Responder Chain and UIEvent


The Responder Chain is also involved in touching the screen, movements, clicks. When the system detects any events (touch, motion, remote-control, press), a UIEvent is created under the hood and sent using the UIApplication.shared.sendEvent() method to UIWindow . After receiving the event, UIWindow determines using the hitTest:withEvent to which UIResponder this event belongs and assigns it the first responder . Next is the work with a linked list of UIResponder described above.


To work with system UIEvent , subclasses of UIResponder (UIViewController, UIView, UIApplication) can override these methods:


 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?) 

Despite the fact that the ability to inherit and call sendEvent manually is present, UIResponder not intended for this. This can create a lot of problems with the work of custom events, which can lead to incomprehensible actions caused by an accidental first responeder that can respond to your event.


Why it is useful, where to use


Despite the fact that the iOS Responder Chain fully controlled by UIKit , it can be used to solve the problem of delegation / communication. UIResponder action is similar to the one-time NotificationCenter.default.post .


Let's take an example, we have a UIViewController , which is deep in the UINavigationController stack and we need to tell it what happened when a button was clicked on another screen. You can use the delagate pattern or NotificationCenter.default.post , but a fairly simple option is to use the iOS Responder Chain .


 button.addTarget(nil, action: #selector(RootVC.doSomething), for: .touchUpInside) 

When pressed, the method in the UIViewController will be called. #selector can take the following parameters:


 func doSomething() func doSomething(sender: Any?) func doSomething(sender: Any?, event: UIEvent?) 

sender is the object that sent the event - UIButton, UITextField, and so on.


Additional Resources for Learning [eng]:


Good description of UIEvent, UIResponder and a couple of advanced examples (patern coordinator)
Read more about ios responder chain
Practical example of a responder chain
Off dock on iOS responder chain
Off Dock by UIResponder

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


All Articles