Detecting continuous key presses with UIKeyCommand

4k views Asked by At

Is it possible to get continuous key presses?

I'm using keyCommands: to intercept the arrows keys being pressed on an external keyboard, but I only get 1 call per press. I would love to get either multiple calls every X milliseconds as long as the key is held down, or calls when the key is pressed and when it’s released.

Here's my code:

- (NSArray *)keyCommands
{
    UIKeyCommand *upArrow = [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
                                                modifierFlags:0
                                                       action:@selector(upArrow:)];
    UIKeyCommand *downArrow = [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow
                                                  modifierFlags:0
                                                         action:@selector(downArrow:)];
    UIKeyCommand *leftArrow = [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
                                                  modifierFlags:0
                                                         action:@selector(leftArrow:)];
    UIKeyCommand *rightArrow = [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
                                                   modifierFlags:0
                                                          action:@selector(rightArrow:)];
    UIKeyCommand *leftArrowCmd = [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
                                                     modifierFlags:UIKeyModifierCommand
                                                            action:@selector(leftArrowCmd:)];
    UIKeyCommand *rightArrowCmd = [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
                                                      modifierFlags:UIKeyModifierCommand
                                                             action:@selector(rightArrowCmd:)];
    UIKeyCommand *lCmd = [UIKeyCommand keyCommandWithInput:@"l"
                                             modifierFlags:UIKeyModifierCommand
                                                    action:@selector(lCmd:)];
    UIKeyCommand *rCmd = [UIKeyCommand keyCommandWithInput:@"r"
                                             modifierFlags:UIKeyModifierCommand
                                                    action:@selector(rCmd:)];
    UIKeyCommand *mCmd = [UIKeyCommand keyCommandWithInput:@"m"
                                             modifierFlags:UIKeyModifierCommand
                                                    action:@selector(mCmd:)];
    return @[upArrow, downArrow, leftArrow, rightArrow, leftArrowCmd, rightArrowCmd, lCmd, rCmd, mCmd];
}
4

There are 4 answers

2
Christian A. Strømmen On BEST ANSWER

Seems that this is indeed not possible in iOS 7.

5
Lars Forsberg On

It is possible to get continuous key presses since the method keyCommands is first called when you press a key on the keyboard, then called again after the key has been released. However, you can't tell if the user is releasing a key or pressing another key while still holding down the first key, so there are limitations to what you can do.

This limitation should be fine as long as your app does not require that the user can press and hold multiple keys at the same time. If you can assume that the user is only pressing one key at a time, you can use this to get continuous key presses.

EDIT: I got the comment that this is seems to be a false statement. That is wrong! Here is a complete example:

#import "ViewController.h"

@interface ViewController ()
{
    bool keyDown;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    keyDown = NO;
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (NSArray*)keyCommands
{
    if(keyDown==YES) {
        printf("Key released\n");
        keyDown = NO;
    }
    UIKeyCommand *upArrow = [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
                                                modifierFlags:0
                                                       action:@selector(upArrow:)];
    return @[upArrow];
}

-(void)upArrow:(UIKeyCommand*)keyCommand
{
    printf("Key pressed\n");
    keyDown = YES;
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}
@end

When you press the up-arrow button, you will see the text "Key pressed" in the output console. When you release it, you will see the text "Key released". As I mentioned, there's one limitation. If you hold the up-arrow and then at the same time press another key, the code will assume the up-arrow key has been released.

This is what happens when you press and hold the up arrow key:

  1. First of all, the method keyCommands will be called twice (why twice?)
  2. The upArrow method will be called once and the variable keyDown will be set to YES and the text "Key pressed" will be printed.

This is what happens when you release the key:

  1. The method keyCommands will be called one time. The text "Key released" will be printed since keyDown=YES.

As I mentioned above, there's a limitation with this method; if you first press up arrow and hold it while pressing another button, this code will believe that you have released the up arrow button. You obviously need to wrap this up with a timer to get continuous key presses in your app.

0
JFo On

Pro tip - my arrow keys were blocked because I'd returned false from

-(BOOL) canPerformAction:(SEL)action withSender:(id)sender 

Make sure to check for your own selectors you've created.

1
Tarun Gehlaut On

With iOS 13.4+ it is now possible. UIPress now has a UIKey member variable

Refer to this official tutorial