iosiphoneipaduikeycommand

Stop UIKeyCommand repeated actions


If a key command is registered, it's action might be called many times if the user holds down the key too long. This can create very weird effects, like ⌘N could repeatedly open a new view many times. Is there any easy way to stop this behavior without resorting to something like a boolean "already triggered" flag?

Here's how I register two different key commands:

#pragma mark - KeyCommands

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (NSArray<UIKeyCommand *>*)keyCommands {
    return @[
             [UIKeyCommand keyCommandWithInput:@"O" modifierFlags:UIKeyModifierCommand action:@selector(keyboardShowOtherView:) discoverabilityTitle:@"Show Other View"],
             [UIKeyCommand keyCommandWithInput:@"S" modifierFlags:UIKeyModifierCommand action:@selector(keyboardPlaySound:) discoverabilityTitle:@"Play Sound"],
             ];
}

- (void)keyboardShowOtherView:(UIKeyCommand *)sender {
    NSLog(@"keyboardShowOtherView");
    [self performSegueWithIdentifier:@"showOtherView" sender:nil];
}

- (void)keyboardPlaySound:(UIKeyCommand *)sender {
    NSLog(@"keyboardPlaySound");
    [self playSound:sender];
}

#pragma mark - Actions

- (IBAction)playSound:(id)sender {
    AudioServicesPlaySystemSound(1006); // Not allowed in the AppStore
}

A sample project can be downloaded here: TestKeyCommands.zip


Solution

  • In general, you don't need to deal with this, since the new view would usually become the firstReponder and that would stop the repeating. For the playSound case, the user would realize what is happening and take her finger off of the key.

    That said, there are real cases where specific keys should never repeat. It would be nice if Apple provided a public API for that. As far as I can tell, they do not.

    Given the '//Not allowed in the AppStore' comment in your code, it seems like you're OK using a private API. In that case, you could disable repeating for a keyCommand with:

    UIKeyCommand *keyCommand =  [UIKeyCommand ...];
    [keyCommand setValue:@(NO) forKey:@"_repeatable"];