UIKeyCommand | NSHipster


title: UIKeyCommand author: Nate Cook category: Cocoa excerpt: "As part of the push for greater productivity on the iPad, iOS 9 adds Discoverability, an overlay showing the currently available key commands inside an app. This small change suddenly makes key commands far more viable on the iPad and, with it, makes UIKeyCommand a necessary addition to your app." status: swift: 2.0

Adding a new feature to a product is always a tradeoff. Will the added utility of a new feature be enough to offset the added complexity? Shortcuts would seem to side-step this issue—after all, they're simply a quicker alternative for features already in your app. But that creates another dilemma: what if a new feature is added and no one knows it's there?

When key commands for external keyboards debuted in iOS 7, there was no intrinsic way to learn of their existence. Unlike in OS X, where a user can gradually discover shortcuts for the menu items they use most often, an iOS app had few ways to communicate what key commands are available. Initial tours flash by and fade from memory; help screens are hidden out of sight. Without a way to make shortcuts visible in a timely and relevant manner, users were sure to miss out on useful features that developers had taken the time to implement.

No longer. As part of the push for greater productivity on the iPad, iOS 9 adds Discoverability, an overlay showing the currently available key commands inside an app. This small change suddenly makes key commands far more viable on the iPad and, with it, makes UIKeyCommand a necessary addition to your app.


UIKeyCommand

The UIKeyCommand class is in fact quite simple, with only four properties to configure:

Responding to Key Commands

Enabling key commands is as simple as providing an array of UIKeyCommand instances somewhere in the responder chain. Text inputs are automatically first responders, but perhaps more usefully, a view controller can respond to key commands by implementing canBecomeFirstResponder():

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

Next, provide a list of available key commands via the keyCommands property:

override var keyCommands: [UIKeyCommand]? {
    return [
        UIKeyCommand(input: "1", modifierFlags: .Command, action: "selectTab:", discoverabilityTitle: "Types"),
        UIKeyCommand(input: "2", modifierFlags: .Command, action: "selectTab:", discoverabilityTitle: "Protocols"),
        UIKeyCommand(input: "3", modifierFlags: .Command, action: "selectTab:", discoverabilityTitle: "Functions"),
        UIKeyCommand(input: "4", modifierFlags: .Command, action: "selectTab:", discoverabilityTitle: "Operators"),
            
        UIKeyCommand(input: "f", modifierFlags: [.Command, .Alternate], action: "search:", discoverabilityTitle: "Find…"),
    ]
}

<#...#>

func selectTab(sender: UIKeyCommand) {
    let selectedTab = sender.input
    <#...#>
}
- (NSArray<UIKeyCommand *>*)keyCommands {
    return @[
        [UIKeyCommand keyCommandWithInput:@"1" modifierFlags:UIKeyModifierCommand action:@selector(selectTab:) discoverabilityTitle:@"Types"],
        [UIKeyCommand keyCommandWithInput:@"2" modifierFlags:UIKeyModifierCommand action:@selector(selectTab:) discoverabilityTitle:@"Protocols"],
        [UIKeyCommand keyCommandWithInput:@"3" modifierFlags:UIKeyModifierCommand action:@selector(selectTab:) discoverabilityTitle:@"Functions"],
        [UIKeyCommand keyCommandWithInput:@"4" modifierFlags:UIKeyModifierCommand action:@selector(selectTab:) discoverabilityTitle:@"Operators"],

        [UIKeyCommand keyCommandWithInput:@"f" 
                            modifierFlags:UIKeyModifierCommand | UIKeyModifierAlternate 
                                   action:@selector(search:) 
                     discoverabilityTitle:@"Find…"]
    ];
}

<#...#>

- (void)selectTab:(UIKeyCommand *)sender {
    NSString *selectedTab = sender.input;
    <#...#>
}

In the Discoverability layover, accessed by holding down the Command key, key commands are listed in the order you specified:

Discoverability Layover
Discoverability Layover

Voila! Secrets, revealed!

Context Sensitivity

The keyCommands property is accessed whenever a key pressed, making it possible to provide context-sensitive responses depending on the state of your application. While this is similar to the way a menu item and its active/inactive state are configured in OS X, the recommendation for iOS is to omit inactive commands completely—that is, there are no grayed out commands in the Discoverability layover.

Here, a set of commands that are available to logged in users of an app are included only when appropriate:

let globalKeyCommands = [UIKeyCommand(input:...), ...]
let loggedInUserKeyCommands = [UIKeyCommand(input:...), ...]

override var keyCommands: [UIKeyCommand]? {
    if isLoggedInUser() {
        return globalKeyCommands + loggedInUserKeyCommands
    } else {
        return globalKeyCommands
    }
}

Although we don't take shortcuts when creating our apps, that doesn't mean our users won't find shortcuts useful. Adding key commands lets control of your app shift from the screen to the keyboard—your users will love the option.