12

I know this has been asked before, and the only answers I've seen are "Don't require an external keyboard, as it goes against UI guidelines". However, I want to use a foot pedal like this: http://www.bilila.com/page_turner_for_ipad to change between pages in my app (in addition to swiping). This page turner emulates a keyboard and uses the up/down arrow keys.

So here is my question: how do I respond to these arrow key events? It must be possible as other apps manage, but I'm drawing a blank.

colincameron
  • 2,696
  • 4
  • 23
  • 46

2 Answers2

29

For those who are looking for a solution under iOS 7 - there is a new UIResponder property called keyCommands. Create a subclass of UITextView and implement keyCommands as follows...

@implementation ArrowKeyTextView

- (id) initWithFrame: (CGRect) frame
{
    self = [super initWithFrame:frame];
    if (self) {
    }
    return self;
}

- (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:)];
    return [[NSArray alloc] initWithObjects: upArrow, downArrow, leftArrow, rightArrow, nil];
}

- (void) upArrow: (UIKeyCommand *) keyCommand
{

}

- (void) downArrow: (UIKeyCommand *) keyCommand
{

}

- (void) leftArrow: (UIKeyCommand *) keyCommand
{

}

- (void) rightArrow: (UIKeyCommand *) keyCommand
{

}
amergin
  • 3,106
  • 32
  • 44
  • @Altaveron the keyCommands property is a property on UIResponder and UIWebView inherits from UIResponder so I can see no reason why it won't work. – amergin Jan 04 '14 at 14:43
  • @Altaveron have you tried it with a UIWebView and, if so, what result are you seeing? – amergin Jan 05 '14 at 00:27
  • This is perfect! But how would you get a continuous input? Say the user holds down the key, and we want to detect to do something until he releases it? – Christian A. Strømmen Jan 09 '14 at 12:20
  • @Christian the UIKeyCommand class doesn't give you this control but the keyboard should start sending out repeated keypresses after a short delay. If it does then you can presumably just read this stream of data until it changes or stops flowing. – amergin Jan 09 '14 at 13:37
  • On `UIWebView` `keyCommands` works but `@selectors` don't execute. How can I solve this? – Dmitry Jan 09 '14 at 19:13
  • This is super helpful! Any way to combine modifiers, e.g. Cmd+Shift+Right Arrow? – R.S Jan 14 '14 at 04:29
  • I had tried this, and just retried it - doesn't seem to work for me. Have you tested it? – R.S Jan 15 '14 at 05:14
  • Sorry @R.S. stupid error on my part, it should read (UIKeyModifierCommand | UIKeyModifierShift) - it's a bitwise OR not an AND! – amergin Jan 15 '14 at 10:31
  • keyCommands does work but the event is called both when the key is depressed and when it is released. There does not seem to be any legal way to discriminate between the two. – Glyn Williams Mar 09 '15 at 14:15
9

Sorted! I simply use a 1x1px text view and use the delegate method textViewDidChangeSelection:

EDIT: For iOS 6 I had to change the text view to 50x50px (or at least enough to actually display text) for this to work

I also managed to suppress the on-screen keyboard when the pedal is disconnected.

This is my code in viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillDisappear:) name:UIKeyboardWillHideNotification object:nil];

UITextView *hiddenTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[hiddenTextView setHidden:YES];
hiddenTextView.text = @"aa";
hiddenTextView.delegate = self;
hiddenTextView.selectedRange = NSMakeRange(1, 0);
[self.view addSubview:hiddenTextView];

[hiddenTextView becomeFirstResponder];
if (keyboardShown)
    [hiddenTextView resignFirstResponder];

keyboardShown is declared as a bool in my header file.

Then add these methods:

- (void)textViewDidChangeSelection:(UITextView *)textView {

    /******TEXT FIELD CARET CHANGED******/

    if (textView.selectedRange.location == 2) {

        // End of text - down arrow pressed
        textView.selectedRange = NSMakeRange(1, 0);

    } else if (textView.selectedRange.location == 0) {

        // Beginning of text - up arrow pressed
        textView.selectedRange = NSMakeRange(1, 0);

    }

    //  Check if text has changed and replace with original
    if (![textView.text isEqualToString:@"aa"])
        textView.text = @"aa";
}

- (void)keyboardWillAppear:(NSNotification *)aNotification {
    keyboardShown = YES;
}

- (void)keyboardWillDisappear:(NSNotification *)aNotification {
    keyboardShown = NO;
}

I hope this code helps someone else who is looking for a solution to this problem. Feel free to use it.

colincameron
  • 2,696
  • 4
  • 23
  • 46
  • 1
    Worked great for me, but I had to put the initialization code in viewDidAppear not viewDidLoad. – Bryan Aug 18 '12 at 15:49
  • 2
    I think this leads to an infinite loop, because setting `selectedRange` property (in `textViewDidChangeSelection`) triggers another `textViewDidChangeSelection`, although this idea really helped me. – Bianca Daniciuc Jul 12 '13 at 12:34
  • It won't cause an infinite loop, as `selectedRange` is only set if the cursor location is 0 or 2. – colincameron Jul 15 '13 at 15:23
  • 3
    I realise this is old, but in case anyone else comes across it - there's a better way to keep the keyboard hidden on an input view like this. Simply add a line: `textView.inputView = [[UIView alloc] initWithFrame:CGRectZero];` – Gabriel Nov 08 '13 at 01:11
  • Nice idea. But it responds on all the keys, but I need only left and right arrow keys. – zinnuree Nov 19 '14 at 10:02