Event Delivery on iOS: Part 2

In the first part of this series, we talked about touch handling. Now, let’s dive deeper into how the Responder Chain works as well as review the other types of events that can be handled in an iOS application: motion events, remote control events, and key commands.

More on the Responder Chain

We briefly outlined the Responder Chain while covering touch events, but let’s take a closer look. The Responder Chain is the primary mechanism for delivering these other types of events throughout your application.

The concept of the Responder Chain is simple. It consists of a chain of UIResponder objects. Each element in the chain gets a chance to respond to a given message. If the given responder doesn’t respond to the message, the next item in the chain gets a chance to respond. If no objects in the chain respond to the message, the message is discarded.

The first object that is given the opportunity to respond to a message is called the “first responder.” The first responder is the most recent object to invoke -[UIResponder becomeFirstResponder]. An object can become the first responder by overriding -[UIResponder canBecomeFirstResponder] and returning YES. When an object is finished being the first responder, invoke -[UIResponder resignFirstResponder].

Being a responder to motion events and remote control events is fairly straightforward. The object that is the first responder gets sent the motion or remote control message. It then has the opportunity to respond to the event, send the event down the Responder Chain, or do both by handling the event and then invoking the method on the superclass. The methods involved are defined in UIResponder. Apple’s class reference for UIResponder is a great source for more information.

In Practice

We’re to the point where you’re probably asking, “This is all good, but where can I use this in practice?” Let’s take a look at a sample application. To get started, clone the GitHub repository.

This application isn’t very useful, but it demonstrates a simple example of how the Responder Chain works. It requires a hardware keyboard and only does one thing: prints the letter “a” inside a gray box. It runs fine in the simulator or any iOS device with a connected keyboard. Let’s dive into some of the code.

First, let’s look at the main view controller:

class ViewController: UIViewController {
@IBOutlet weak var customTextView: CustomTextView!

@IBAction func textViewTapped(_ sender: AnyObject) {
customTextView.becomeFirstResponder()
}
}

This view controller is simple. It contains a custom view of type CustomTextView and handles an action from a tap gesture recognizer that is configured in the storyboard. The tap gesture handler invokes becomeFirstResponder on customTextView. Let’s look at CustomTextView now.

class CustomTextView: UIView {
private var textToRender: NSString? {
didSet {
setNeedsDisplay()
}
}

func render(text: NSString) {
textToRender = text
}

override func draw(_ rect: CGRect) {
super.draw(rect)

textToRender?.draw(at: .zero, withAttributes: nil)
}

override var canBecomeFirstResponder: Bool {
return true
}

override var canResignFirstResponder: Bool {
return true
}

override var keyCommands: [UIKeyCommand]? {
var array = [UIKeyCommand]()

let keyCommand = UIKeyCommand(input: "a",
modifierFlags: [],
action:
#selector(KeyCommandReceiver.aKeyPressed(sender:)))

array.append(keyCommand)
return array
}
}

This view is a tad more complex. Let’s walk through it from the top. First, it defines a textToRender property and invokes setNeedsDisplay() when it is set. There’s a render(text:) method that sets the text that’s passed in to textToRender. This view overrides draw(_:), which simply draws the text on the view without any attributes.

The next part is where UIResponder comes in. The view can become the first responder as well as resign its first responder status. Then it defines an array of keyCommands. Currently, it defines one key command. It responds to the “a” key being pressed and has an action defined as #selector(KeyCommandReceiver.aKeyPressed). KeyCommandReceiver is a protocol with this definition:

@objc protocol KeyCommandReceiver: class {
func aKeyPressed(sender: AnyObject?)
}

All this defines is the message that gets sent when the key command is triggered. When the key command is triggered, the command is sent to the responder chain starting with the first responder. In our application, we don’t want the view to handle the event so we implement the method on the view controller.

extension ViewController: KeyCommandReceiver {
func aKeyPressed(sender: AnyObject?) {
customTextView.render(text: "a")
}
}

What this demonstrates is how the Responder Chain works in action. According to the documentation, the action of a UIKeyCommand starts at the first responder and traverses the Responder Chain until the action is handled. If the action isn’t handled, it silently fails.

Final Notes

What happens if there isn’t a first responder? The system will figure out where actions should go based on the current view and view controller hierarchy. From my experience, event delivery to a certain responder isn’t guaranteed if there is no first responder. For example, simple view controller containment with one view controller contained in another view controller with no “siblings” results in a simple chain that goes from the child view controller to its parent. A situation where you have a parent view controller that contains multiple child view controllers will result in the children getting skipped while the system figures out where the Responder Chain starts.

If you want to have a deterministic path through the Responder Chain, define a first responder by invoking becomeFirstResponder. You can also change the order of the Responder Chain by overriding nextResponder and returning the next part of the chain. Be careful doing this, since you can completely break the Responder Chain and cause some undefined behavior.

Wrapping Up

Understanding the event flow is vital when you need to implement handlers for motion events, remote control events, or key commands. This knowledge is very useful when architecting your application.

In Part 3, we’ll look at another pattern used for handling user interaction. We’ll also talk about how to use the Responder Chain for custom events in your applications.


For more insights on design and development, subscribe to BPXL Craft and follow Black Pixel on Twitter.