Detecting continuous key presses with UIKeyCommand
Asked Answered
A

4

15

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];
}
Alfalfa answered 9/1, 2014 at 12:51 Comment(0)
A
6

Seems that this is indeed not possible in iOS 7.

Alfalfa answered 14/1, 2014 at 10:44 Comment(2)
See Lars Forsberg answer bellow.Adiaphorism
@Adiaphorism this 'solution' relies on undocumented behavior and may break at any moment. Using this approach is highly discouraged.Mace
W
4

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.

Wailful answered 9/1, 2015 at 9:52 Comment(5)
Is this new in iOS 8? This wasn't the case when I tested this.Sherronsherry
It is not false and I have this working in my own app VividTracker. What exactly do you mean is false?Wailful
I have now added a complete example that shows that this works. You can run it in the simulator. It also works when deploying to iOS 7.1.Wailful
I didn't read your answer closely enough.. I've confirmed that what you said is indeed correct. My apologies. Thanks for the example.Adiaphorism
This behavior is not documented and may break any moment.Mace
H
4

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.

Hyperbola answered 26/10, 2015 at 6:55 Comment(0)
G
1

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

Refer to this official tutorial

Goda answered 12/8, 2020 at 16:45 Comment(1)
You get pressesBegan and pressesEnded, but there is still no functionality when a key is held down. You still need your own timer or did I miss something?Iorio

© 2022 - 2024 — McMap. All rights reserved.