Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I support the up and down arrow keys with a Bluetooth keyboard under iOS 7

Under iOS 6, up and down arrow keys were respected by UITextView when using an external Bluetooth keyboard and when working in the simulator. Under iOS 7 the up and down arrow keys no longer do anything, although the left and right arrow keys still move the cursor.

How do you support up and down arrow keys from an external keyboard in UITextView under iOS 7?

like image 548
Mike Avatar asked Oct 07 '13 22:10

Mike


People also ask

How do I use the arrow keys on my iPad Bluetooth keyboard?

Navigating Screens with a Bluetooth Keyboard Assuming QuickNav is enabled, you can then press your Right Arrow key to do the equivalent of a one finger flick right, and the Left Arrow key to move to the left. If the Arrow keys aren't working, press the Left and Right Arrow keys together to enabled QuickNav.

How do I get the arrow keys on my Apple keyboard?

Answer: A: Open keyboard viewer (Open System Preferences>Keyboard>Input sources and click on show input in menu bar, then click on the keyboard icon in menu bar and select show keyboard viewer. ) Press on the arrow keys and side if the virtual keyboard shows those keys being depressed.

Does iPhone support Bluetooth keyboard?

You can use Magic Keyboard, including Magic Keyboard with Numeric Keypad, to enter text on iPhone. Magic Keyboard connects to iPhone using Bluetooth.

How do I navigate my iPhone using an external keyboard?

Control iPhone with an external keyboardGo to Settings > Accessibility > Keyboards, tap Full Keyboard Access, then turn on Full Keyboard Access. Control your iPhone using keyboard shortcuts. To customize the keyboard shortcuts, tap Commands.


2 Answers

I have no idea if the omission of up and down arrow key support is intentional in iOS 7. Here's a way to restore about 95% of the lost capability. Note that this subclass of UITextView also corrects a few issues in iOS 7 where scrolling a text view causes it to jump wildly. I've seen it jump from midway through a long file to the end of the file.

The only problem I have found with this implementation is that the repeat key os not handled, so if you hold the up or down arrow keys down, the code to move the cursor is still only called one time.

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

@implementation ArrowKeyTextView

- (id) initWithFrame: (CGRect) frame {
    self = [super initWithFrame:frame];
    if (self) {
        // This has nothing to do with support for arrow keys, but it does fix a number of issues with scrolling in iOS 7.
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
            self.layoutManager.allowsNonContiguousLayout = NO;
    }
    return self;
}

- (NSArray *) keyCommands {
    UIKeyCommand *upArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputUpArrow modifierFlags: 0 action: @selector(upArrow:)];
    UIKeyCommand *downArrow = [UIKeyCommand keyCommandWithInput: UIKeyInputDownArrow modifierFlags: 0 action: @selector(downArrow:)];
    return [[NSArray alloc] initWithObjects: upArrow, downArrow, nil];
}

- (void) upArrow: (UIKeyCommand *) keyCommand {
    UITextRange *range = self.selectedTextRange;
    if (range != nil) {
        float lineHeight = self.font.lineHeight;

        CGRect caret = [self firstRectForRange: range];
        if (isinf(caret.origin.y)) {
            // Work-around for a bug in iOS 7 that returns bogus values when the caret is at the start of a line.
            range = [self textRangeFromPosition: range.start toPosition: [self positionFromPosition: range.start offset: 1]];
            caret = [self firstRectForRange: range];
            caret.origin.y = caret.origin.y + lineHeight;
        }
        caret.origin.y = caret.origin.y - lineHeight < 0 ? 0 : caret.origin.y - lineHeight;
        caret.size.width = 1;
        UITextPosition *position = [self closestPositionToPoint: caret.origin];
        self.selectedTextRange = [self textRangeFromPosition: position toPosition: position];

        caret = [self firstRectForRange: self.selectedTextRange];
        if (isinf(caret.origin.y)) {
            // Work-around for a bug in iOS 7 that occurs when the range is set to a position past the end of the last character
            // on a line.
            NSRange range = {0, 0};
            range.location = [self offsetFromPosition: self.beginningOfDocument toPosition: position];
            self.selectedRange = range;
        }
   }
}

- (void) downArrow: (UIKeyCommand *) keyCommand {
    UITextRange *range = self.selectedTextRange;
    if (range != nil) {
        float lineHeight = self.font.lineHeight;

        CGRect caret = [self firstRectForRange: range];
        if (isinf(caret.origin.y)) {
            // Work-around for a bug in iOS 7 that returns bogus values when the caret is at the start of a line.
            range = [self textRangeFromPosition: range.start toPosition: [self positionFromPosition: range.start offset: 1]];
            caret = [self firstRectForRange: range];
            caret.origin.y = caret.origin.y + lineHeight;
        }
        caret.origin.y = caret.origin.y + lineHeight < 0 ? 0 : caret.origin.y + lineHeight;
        caret.size.width = 1;
        UITextPosition *position = [self closestPositionToPoint: caret.origin];
        self.selectedTextRange = [self textRangeFromPosition: position toPosition: position];

        caret = [self firstRectForRange: self.selectedTextRange];
        if (isinf(caret.origin.y)) {
            // Work-around for a bug in iOS 7 that occurs when the range is set to a position past the end of the last character
            // on a line.
            NSRange range = {0, 0};
            range.location = [self offsetFromPosition: self.beginningOfDocument toPosition: position];
            self.selectedRange = range;
        }
    }
}

@end
like image 192
Mike Avatar answered Oct 19 '22 23:10

Mike


This worked for me except for this part in downArrow:

    CGRect caret = [self firstRectForRange: range];
    if (isinf(caret.origin.y)) {
        // Work-around for a bug in iOS 7 that returns bogus values when the caret is at the start of a line.
        range = [self textRangeFromPosition: range.start toPosition: [self positionFromPosition: range.start offset: 1]];
        caret = [self firstRectForRange: range];
        caret.origin.y = caret.origin.y + lineHeight;
    }

I had to remove the if line:

    CGRect caret = [self firstRectForRange: range];
    // Work-around for a bug in iOS 7 that returns bogus values when the caret is at the start of a line.
    range = [self textRangeFromPosition: range.start toPosition: [self positionFromPosition: range.start offset: 1]];
    caret = [self firstRectForRange: range];
    caret.origin.y = caret.origin.y + lineHeight;

and it works like a charm!

like image 34
Ricardo Arguello Avatar answered Oct 19 '22 23:10

Ricardo Arguello