Skip to content

Posts tagged ‘keyboard’

3
Apr

How and Why to Implement Keyboard Shortcuts in iOS 7

Support for keyboard shortcuts is new in iOS 7. The new UIKeyCommand class and the -[UIResponder keyCommands] method allow app developers to add keyboard shortcuts to their iOS apps without using ugly hacks.

Two Reasons

There are two reasons to add keyboard shortcuts to your iOS app:

  1. People often use wireless keyboads with their iPads, especially for text entry. Keyboard shortcuts for an external keyboard are helpful additions that can make typing on an iOS device more convenient.
  2. They work in the iOS Simulator! During development and testing, keyboard shortcuts are so much nicer than using the mouse to simulate gestures on the simulator.

I admit that second reason is a benefit only during development. However, keyboard shortcuts make using the simulator so much nicer that, ten minutes after adding them to my app, I was kicking myself for not adding them sooner.

Key Commands

The first thing to do is to implement the keyCommands method in one of your classes in the responder chain. On iOS, the responder chain includes the currently focused control, its super views, and their associated view controllers.

@implementation MyViewController

- (NSArray *)keyCommands {
    return @[
        [UIKeyCommand keyCommandWithInput:@"f" modifierFlags:0 action:@selector(keyPressF)]
    ];
}

@end

Starting at the first responder and working up the responder chain, iOS will ask each object if it responds to the given action. The first object it finds that implements the method is allowed to handle the key press. This means that you can implement the keyCommands method in a different class than you define the action handler methods. However, to avoid compiler warnings about private methods, it’s often easier to do both in the same class.

The keyCommands method appears to be called three times for each key press, so you may want to cache the array if you have a lot commands.

First Responders

By default, iOS will only generate key press events when a control, such as a text field, is the first responder. You can fix that by overriding the canBecomeFirstResponder method in your root view controller so that it can become the first responder. You can implement the keyCommands method and your action methods there too.

@implementation MyRootViewController

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (NSArray *)keyCommands {
    return @[
        [UIKeyCommand keyCommandWithInput:@"f" modifierFlags:0 action:@selector(keyPressF)]
    ];
}

- (void)keyPressF {
    // Do something awesome here.
}

@end

This lets you avoid having to create and manage a hidden text field or something similar.

Handling Dialogs

For simple apps, that’s all you need to do. However, if your app presents a modal dialog with a text field, then your root view controller will not regain first responder status when that dialog is dismissed.

You could manually tell your root view controller to become the first responder each time a dialog is dismissed. But there is an easier way.

UIApplication is the top-level object in the responder chain. If you subclass UIApplication and override the canBecomeFirstResponder method, then it will become the first responder when a dialog is dismissed. Then it will tell your root view controller to become the first responder again.

@implementation MyApplication

- (BOOL)canBecomeFirstResponder {
    return YES;
}

@end

To tell iOS to use your new UIApplication subclass, you need to modify the main method in main.m:

int main(int argc, char *argv[]) {
    @autoreleasepool {
        // The third parameter is nil by default.
        return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class], NSStringFromClass([AppDelegate class]));
    }
}

Now a dialog with a text field will no longer mess up your keyboard shortcuts.

Simplifying Things

Things are working, but you can simplify a bit more if you want. Instead of having an app delegate separate from your UIApplication subclass, you can let your subclass be its own delegate.

Then you can move everything from your app delegate into your new MyApplication class, including the properties and app lifecycle methods, and delete your app delegate class.

@interface MyApplication : UIApplication <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Then adjust main.m one more time to assign the same class as both the application and its delegate:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSString *app = NSStringFromClass([MyApplication class]);
        return UIApplicationMain(argc, argv, app, app);
    }
}

Let me know if I’ve missed something or there are better ways to do this. :)

Future Hopes

As of iOS 7, the UIApplicationDelegate is in the responder chain, and I think it would be better to do this stuff there than subclassing UIApplication. But right now the UIApplicationDelegate does not appear to forward first responder status to the root view controller. So this only works with a UIApplication subclass.

4
Aug

How To Type Curly Quotes In Mac OS X

Mac OS X has an easy way to type “curly” quotes and apostrophes instead of "straight" versions. I used both versions in that sentence to show the difference. Here is a bigger version to make the distinction more visible:

Many people think “curly” quotes look better than "straight" ones.

You can use the following keyboard shortcuts to type a single or double curly quote:

  1. Single quote open (‘) — option ]
  2. Single quote close (’) — shift option ]
  3. Double quote open (“) — option [
  4. Double quote close (”) — shift option [

However, I think it makes more sense to use [ and ] for open and close versions instead of the shift key. I found myself constant typing “mismatched‘ quotes. I also wanted to use the shift key for double quotes since that’s how the normal keyboard button works.

  1. Single quote open (‘) — option [
  2. Single quote close (’) — option ]
  3. Double quote open (“) — option shift [
  4. Double quote close (”) — option shift ]

Since OS X supports custom key bindings, I looked for a way to fix this. The trick is to create a file called DefaultKeyBinding.dict in the KeyBindings folder inside your Library folder. You can use this file to override the default key bindings for most applications.

Here are my changes. Please feel free to copy the settings below and save them to your own computer. You may need to create the KeyBindings folder if it isn’t already there.

/*
 Updates Apple's default keybindings for curly quotes.
 See http://www.danandcheryl.com/2010/08/how-to-type-curly-quotes-in-mac-os-x

 Save this file here:
 /Users/<name>/Library/KeyBindings/DefaultKeyBinding.dict
*/
{
    "~[" = ("insertText:", "‘");
    "~]" = ("insertText:", "’");
    "~{" = ("insertText:", "“");
    "~}" = ("insertText:", "”");
}