• IOS Responder Chain: UIResponder, UIEvent, UIControl and uses
  • Bruno Rocha
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: iWeslie
  • Proofread by: Swants

Who is the first responder when I use UITextField? Why is UIView subclassed like UIResponder? What’s the key?

In iOS, a responder chain is a linked list of UIResponder objects generated by UIKit, which is also the basis for all related events in iOS, such as touches and actions.

Responder chains are something you deal with a lot in the iOS development world, and although you rarely need to deal with them directly beyond the keyboard issues of UITextField. Knowing how it works will make it easier or more creative to solve event-related problems, and you can even rely on the chain of responders alone for architecture.

UIResponder, UIEvent, and UIControl

In short, UIResponder instance objects can respond to and handle random events. There are lots of things in iOS like UIView, UIViewController, UIWindow, UIApplication, and UIApplicationDelegate.

In contrast, UIEvent represents a single UIKit event that contains only one type and an optional subclass, which can be touch, dynamic effect, remote control, or pinch, and to be more specific, device shake. When detected a system events, click on the screen, for example, create a UIEvent UIKit internal instance and by calling the UIApplication. Shared. Adds the event queue () distributed it to the system. When an event is pulled from the queue, the first UIResponder that can handle the event is selected within UIKit and sent to the corresponding responder. This selection process varies depending on the event type, where the touch event is sent directly to the View being touched, and other kinds of events are sent to a so-called first responder.

To handle system events, subclasses of UIResponder can override some corresponding methods so that they can handle specific UIEvent types:

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?)
Copy the code

To some extent, you can think of UIEvents as notifications. While UIEvents can be subclassed and sendEvent can be called manually, they don’t really mean you can do so, at least not in the normal way. Since you cannot create custom types, sending custom events is problematic because unexpected responders may incorrectly “handle” your events. However, you can still use them, and in addition to system events, UIResponder can respond to any “event” as a Selector.

This approach gives macOS applications a simple way to respond to “menu” actions, such as select, copy, and paste, because macOS has multiple Windows that make simple agents difficult to implement. In any case, they can also be used for iOS and custom operations, which is exactly what UIControl like UIButton can do to send events after touch. Take a look at one of the following buttons:

let button = UIButton(type: .system)
button.addTarget(myView, action: #selector(myMethod), for: .touchUpInside)
Copy the code

While UIResponder can fully detect touch events, handling them is not easy. So how do you distinguish between different types of touch events?

So that’s what UIControl is good at, these subclasses of UIView abstract the process of handling touch events, and reveal the ability to assign events to a particular touch.

Internally, touching this button produces the following results:

let event = UIEvent(...).// UiKit-generated touch events that contain touch positions and properties.
// Send a touch event.
// Determine which UIView is selected by 'hitTest()'.
// Because UIControl is selected, call:
UIApplication.shared.sendAction(#selector(myMethod), to: myView, from: button, for: event)
Copy the code

When a particular target is sent to sendAction, UIKit will directly try to call the desired Selector on the desired target, and crash if it doesn’t happen, but what if the target is nil?

final class MyViewController: UIViewController {
    @objc func myCustomMethod(a) {
        print("SwiftRocks!")}func viewDidLoad(a) {
        UIApplication.shared.sendAction(#selector(myCustomMethod), to: nil, from: view, for: nil)}}Copy the code

If you run it, you’ll see that the myCustomMethod of MyViewController is called even though the event is sent from a normal UIView without a target.

When you don’t specify a target, UIKit will search for a UIResponder that can handle the operation, just as it did in the simple UIEvent example. In this case, being able to handle the action is related to the following UIResponder methods:

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

By default, this method only checks whether the responder implemented the actual method. The “implementation” method can be done in one of three ways, depending on how much information you need (this applies to any native target/ Action control in iOS) :

func myCustomMethod(a)
func myCustomMethod(sender: Any?)
func myCustomMethod(sender: Any? , event: UIEvent?)
Copy the code

Now, what if the responder does not implement this method? In this case, UIKit uses the following UIResponder methods to determine how to proceed:

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

By default, this will return another UIResponder that may handle the desired operation. This step is repeated until the event is processed or no other options are available. But how does the responder know to whom to route the operation?

The responder chain

As mentioned at the beginning, UIKit handles this problem by dynamically managing a linked list of UIResponder objects. The so-called first responder is only the head node of the list, and if the responder is unable to handle a particular event, the event is recursively sent to the next responder in the list until one of the responders can handle the event or the list traversal ends.

While viewing the actual firstResponder is protected by the private firstResponder property in UIWindow, you can check the responder chain for any given responder by checking if the next property has a value:

 extension UIResponder {
    func responderChain(a) -> String {
        guard let next = next else {
            return String(describing: self)}return String(describing: self) + "- >" + next.responderChain()
    }
}

myViewController.view.responderChain()
// MyView -> MyViewController -> UIWindow -> UIApplication -> AppDelegate
Copy the code

In the last UIViewController action example, UIKit first sends the event to the UIView first responder, but since it didn’t implement myCustomMethod, the view sends the event to the next responder, And then the next UIViewController implements the required method.

Although in most cases the responder chain follows the structural order of the subview, you can customize it to change the general process order. In addition to being able to override the next property to return something else, you can force UIResponder to be the first responder by calling becomeFirstResponder() and cancel by calling resignFirstResponder(). This is usually used in conjunction with the UITextField to display the keyboard. UIResponders can define an optional inputView property that makes the keyboard display only if it is the first responder.

Responder chain custom use

Although the responder chain is handled entirely by UIKit, you can use it to help solve problems in communication or proxies.

To some extent, you can think of the UIResponder action as a one-time notification. If you think about any application, you can add flicker effects to almost every view. To navigate the user through the tutorial. How do you ensure that only the currently active view flashes when this action is triggered? One of the possible solutions is to make each view follow a protocol, or use notifications that are ignored by every view except “currentActiveView”, but responder operations allow you to do this with minimal coding without proxies:

final class BlinkableView: UIView {
    override var canBecomeFirstResponder: Bool {
        return true
    }

    func select(a) {
        becomeFirstResponder()
    }

    @objc func performBlinkAction(a) {
        // Flash animation}}UIApplication.shared.sendAction(#selector(BlinkableView.performBlinkAction), to: nil, from: nil.for: nil)
// Will make the last BlinkableView that called SELECT () blink exactly.
Copy the code

This is very similar to regular notifications, except that notifications trigger each object that registers them, whereas this method fires only the BlinkableView object that is found first on the response chain.

As mentioned earlier, you can even architecture in this way. Coordinator This is the Coordinator framework, which defines an event of a custom type and injects itself into the responder chain:

final class PushScreenEvent: UIEvent {

    let viewController: CoordenableViewController

    override var type: UIEvent.EventType {
        return .touches
    }

    init(viewController: CoordenableViewController) {
        self.viewController = viewController
    }
}

final class Coordinator: UIResponder {

    weak var viewController: CoordenableViewController?

    override var next: UIResponder? {
        returnviewController? .originalNextResponder }@objc func pushNewScreen(sender: Any? , event: PushScreenEvent) {
        letnew = event.viewController viewController? .navigationController? .pushViewController(new, animated:true)}}class CoordenableViewController: UIViewController {

    override var canBecomeFirstResponder: Bool {
        return true
    }

    private(set) var coordinator: Coordinator?
    private(set) var originalNextResponder: UIResponder?

    override var next: UIResponder? {
        return coordinator ?? super.next
    }

    override func viewDidAppear(_ animated: Bool) {
        // Fill in the information in viewDidAppear to ensure UIKit
        // The next responder for this view is configured.
        super.viewDidAppear(animated)
        guard coordinator == nil else {
            return
        }
        originalNextResponder = next
        coordinator = Coordinator() coordinator? .viewController =self}}final class MyViewController: CoordenableViewController {
    / /...
}

// Anywhere else in the app:

let newVC = NewViewController(a)UIApplication.shared.push(vc: newVC)
Copy the code

This let CoordenableViewController hold of its original a responder reference (window), but it rewrite the next let it point to the Coordinator, and the latter also pointed to the window of a responder.

// MyView -> MyViewController -> **Coordinator** -> UIWindow -> UIApplication -> AppDelegate
Copy the code

This allows a Coordinator to receive system events and define a new PushScreenEvent containing information about the new View Controller, We can call the pushNewScreen event handled by these Coordinators to refresh the screen.

With this structure, the UIApplication. Shared. Push (vc: NewVC can be called anywhere in the app, without requiring a single agent or singleton, because UIKit will ensure that only the current Coordinator is notified of the event, thanks to the responder chain.

The example shown here is very theoretical, but I hope it helps you understand the purpose and use of the responder chain.

You can follow the author of this article on Twitter @RocktheBruno and share more tips.

Official Reference Documents

Use responders and responder chains to handle events

  • UIResponder
  • UIEvent
  • UIControl

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.