When adding support for keyboard shortcuts, a seasoned iOS developer might raise an eyebrow when noticing that UIKeyCommand takes a selector for an action, but no target. This is because the keyboard commands1 are not send directly to any of your classes, but they instead are passed along the responder chain – until a UIResponder is found that responds to that selector.

The responder chain typically looks like in the following chart from Apple’s documentation:

Exemplary responder chain
Exemplary responder chain

To implement keyboard shortcuts, you need to implement keyCommands at all the parts along your responder chain that should can provide keyboard shortcuts. The nice thing about is that the keyboard shortcuts available on a screen are all the shortcuts provided along the responder chain, so you can provider shortcuts specific to a view, view controller or globally for your application without any of those pieces being aware of the others. If there’s a clash in keyboard shortcuts then those of the responder further up in the chain are used.

What’s not obvious from the documentation is where the start is of your responder chain when it comes to keyboard shortcuts. If you hold the ⌘ key, what objects get to expose their keyboard shortcuts? By default this is just the bottom part of the figure above, but you can opt-in with your view controllers by implementing canBecomeFirstResponder(). However with that any keyboard shortcuts provided by its view and subviews – for example a table view – won’t be available. This is where the concept of a first responder comes in. You can nominate a view by explicitly calling becomeFirstResponder() on it and the chain will then start there.

In summary:

  1. Add your keyCommands along the responder chain; the keyboard shortcuts are then a union of all of these.
  2. Keep in mind that the selector for a keyboard shortcut will fire for the first responder along that chain that handles it, and processing stops after that.
  3. Adjust the first responder where necessary.

Stumbling blocks

When you hit any stumbling blocks, the important thing to keep in mind that keyboard shortcuts are powered by the responder chain, so have a mental diagram of your app’s chain in your head and be aware that it changes with the current selection/first responder. This also means that when you hold the command key to see your keyboard shortcuts and then execute the command via the keyboard, there are two passes over the responder chain: First to get a list of the available commands, and then again the relevant selector is sent along the chain.

Only UIResponder subclasses participate in this. You could have a class KeyCommandHelper: NSObject that creates a list of shared keyboard shortcuts and exposes the relevant selectors. However when you return those from your view controller’s keyCommands, the key commands themselves know nothing about who created them (i.e., and your KeyCommandHelper). Your command show up when holding ⌘, but when pressing the relevant keyboard shortcuts a selector on your KeyCommandHelper would never called.

So if you do want to have a class participate in this, you can either make it a subclass of UIResponder and then insert the class into your responder chain2, or by implementing those shared keyboard shortcuts further app your responder chain, such as in your app delegate (make sure this then subclasses from UIResponder).

Further reading

  1. More specifically the selectors that fire when the keyboard commands are pressed. 

  2. See ‘Altering the Responder Chain’ in the documentation. Basically this means overriding next and maintaining (weak) references to the other objects in the responder chain.