Search code examples
iosobjective-ckeyboarduikitkeyboard-shortcuts

Using UIKeyCommand to map keyboard shortcuts renders UITextField/View useless


I'm using UIKeyCommand to map certain shortcuts (for example "b", arrow keys, "t", "p", etc.) to a functionality inside my UIViewController subclass. The app is kind of a vector graphics software, which allows addition of text objects inside the canvas. The problem arises when a textView or textField inside the view controller is being edited. While it gets the first responder status, it doesn't receive the shortcut keys (for example writing "beaver" will result in "eaver").

Is there a correct way to handle shortcut keys AND use text objects inside a single view controller?


Solution

  • The solution I found to work best is to go through the responder chain to find the active responder and then check whether it is a UITextField/UITextView or something else. In case it is, return nil from the - (NSArray *)keyCommands method, otherwise return the shortcuts. Here's the code itself:

    @implementation UIResponder (CMAdditions)
    
    - (instancetype)cm_activeResponder {
       UIResponder *activeResponder = nil;
    
       if (self.isFirstResponder) {
           activeResponder = self;
       } else if ([self isKindOfClass:[UIViewController class]]) {
           if ([(UIViewController *)self parentViewController]) {
               activeResponder = [[(UIViewController *)self parentViewController] cm_activeResponder];
           }
    
           if (!activeResponder) {
               activeResponder = [[(UIViewController *)self view] cm_activeResponder];
           }
        } else if ([self isKindOfClass:[UIView class]]) {
            for (UIView *subview in [(UIView *)self subviews]) {
               activeResponder = [subview cm_activeResponder];
               if (activeResponder) break;
            }
        }
    
        return activeResponder;
    }
    
    @end
    

    And this goes inside the keyCommands method:

    - (NSArray *)keyCommands {
       if ([self.cm_activeResponder isKindOfClass:[UITextView class]] || [self.cm_activeResponder isKindOfClass:[UITextField class]]) {
           return nil;
       }
    
       UIKeyCommand *brushTool = [UIKeyCommand keyCommandWithInput:@"b"
                                                     modifierFlags:kNilOptions
                                                            action:@selector(brushToolEnabled)
                                              discoverabilityTitle:NSLocalizedString(@"Brush tool", @"Brush tool")];
    
       UIKeyCommand *groupKey = [UIKeyCommand keyCommandWithInput:@"g"
                                                    modifierFlags:UIKeyModifierCommand
                                                           action:@selector(groupKeyPressed)
                                             discoverabilityTitle:NSLocalizedString(@"Group", @"Group")];
    
       UIKeyCommand *ungroupKey = [UIKeyCommand keyCommandWithInput:@"g"
                                                      modifierFlags:UIKeyModifierCommand|UIKeyModifierShift
                                                             action:@selector(ungroupKeyPressed)
                                               discoverabilityTitle:NSLocalizedString(@"Ungroup", @"Ungroup")];
    
       return @[groupKey, ungroupKey, brushTool];
    }