Input Accessorizing with UIViewController

Mark Adams

When designing a user interface that accepts and displays text input, a common pattern is to have a text field persist on screen. As the only control in the view, it is immediately clear how to enter text and have it displayed. It doesn’t hurt that Apple has been training users to accept this pattern for years in Messages.app.

image

In iPhone OS 3.2, this type of persistent view that is anchored to the keyboard became officially supported with the addition of inputAccessoryView to UIResponder. The documentation for this method encourages subclassing to support custom input accessory views.

The default value of this property is nil. Subclasses that want to attach custom controls to either a system-supplied input view (such as the keyboard) or a custom input view (one you provide in the inputView property) should redeclare this property as readwrite and use it to manage their custom accessory view. When the receiver subsequently becomes the first responder, the responder infrastructure attaches the view to the appropriate input view before displaying it.

To achieve our desired result we can take advantage of the fact that UIViewController is a UIResponder subclass. By overriding the inputAccessoryView and returning an instance of a view, UIViewController will take care of placing that view at the bottom of the screen and animating it appropriately when they keyboard appears or disappears.

Assume that we’ve already defined a UIToolbar subclass called MessageInputAccessoryView to use as our accessory input view. In a real application, this class would be responsible for adding a text field to its hierarchy, managing the input and communicating it back up to the view controller. Implementing such a view is beyond the scope of this post.

The first step to displaying the accessory view is to declare it. We’re going to declare a global, private constant instance of MessageInputAccessoryView. Constants declared in the global scope are lazily initialized by default so we can guarantee that only one instance of this view is ever created and only right when the view controller asks for it.

private let accessoryView = AccessoryView(frame: CGRectZero)

Now we can override inputAccessoryView on MessagesViewController to return our newly declared accessoryView.

class MessagesViewController : UIViewController {
    override var inputAccessoryView: AccessoryView {
        return accessoryView
    }
}

Because this functionality descends from UIResponder, we need to allow MessagesViewController to become the first responder in the responder chain in order to interact with the input accessory view. We can do that by overriding canBecomeFirstResponder() and returning true.

override func canBecomeFirstResponder() -> Bool {
    return true
}

Putting it all together and our MessagesViewController looks like this.

private let accessoryView = AccessoryView(frame: CGRectZero)

class ViewController : UIViewController {
    override var inputAccessoryView: AccessoryView {
        return accessoryView
    }

    override func canBecomeFirstResponder() -> Bool {
        return true
    }
}

The Responder Chain is a powerful pattern employed across UIKit. As a parent class of both UIViewController and UIView, it allows us to do some pretty incredible things with regards to view management and event dispatching. Thanks to this overlooked feature of UIResponder, all of the tedium of managing a view on screen that is keyboard-aware is abstracted away and managed by UIViewController.