14

How to handle arrow key event in Cocoa app?

phimuemue
  • 34,669
  • 9
  • 84
  • 115
eonil
  • 83,476
  • 81
  • 317
  • 516

2 Answers2

12

See this code. I assumed the class is subclass of NSView.

#pragma mark    -   NSResponder

- (void)keyDown:(NSEvent *)theEvent
{
    NSString*   const   character   =   [theEvent charactersIgnoringModifiers];
    unichar     const   code        =   [character characterAtIndex:0];
        
    switch (code) 
    {
        case NSUpArrowFunctionKey:
        {
            break;
        }
        case NSDownArrowFunctionKey:
        {
            break;
        }
        case NSLeftArrowFunctionKey:
        {
            [self navigateToPreviousImage];
            break;
        }
        case NSRightArrowFunctionKey:
        {
            [self navigateToNextImage];
            break;
        }
    }
}

A view should be a first responder to receive events. Maybe this code will be required to support that.

#pragma mark    -   NSResponder
- (BOOL)canBecomeKeyView
{
    return  YES;
}
- (BOOL)acceptsFirstResponder
{
    return  YES;
}

To use this method, the class should be subclass of NSResponder. See the other answer handling without subclassing NSResponder.

eonil
  • 83,476
  • 81
  • 317
  • 516
  • 1
    You also need to be a responder and be in the responder chain. Being a view that accepts first responder is a good way to fulfill this prerequisite. – Peter Hosey May 14 '11 at 09:36
  • The key codes have been the same since the days of yore and aren't likely to change. See [this informative article](http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes) by Peter Hosey. The fact that they're only visible to us in a Carbon header doesn't mean much. CGEvents use them primarily, not the corresponding unicode. – jscs May 16 '11 at 03:53
  • @Josh OK. I understood. But I couldn't find defined key code constant values. To use it reliably, some kind of specification is required. Can I know where the official specification is? – eonil May 16 '11 at 05:13
  • @Eonil: please understand that I wasn't saying there was anything wrong _at all_ with your method. I just wanted to present a different one. – jscs May 16 '11 at 05:41
  • @Josh Oh, of course I do. I didn't know the method so I'm appreciating to you about it :) I just want to make it clear the 'key-code' stuff. If you felt some uncomfortable, sorry for my poor English :( – eonil May 16 '11 at 06:12
  • @Eonil: No, no fault of your English. I was just worried that _my_ written words may have sounded like criticism, and I wanted to make clear that this was not my intent. :) I'm glad to be able to share this information! I wish I knew more, or understood it better, so I could really explain it correctly. – jscs May 16 '11 at 06:19
  • 1
    @Eonil: Just stumbled across [a fairly recent Cocoa-dev thread](http://www.cocoabuilder.com/archive/cocoa/281840-high-level-toolkit-is-it-obsolete-or-not.html) which talks about the deprecation/non-deprecation of various parts of Carbon. It cleared a few things up for me; thought you might be interested. Summary is: GUI Carbon stuff is out, most everything else is still okay, documentation should reflect this but doesn't do such a good job. – jscs Jun 25 '11 at 21:44
  • @Josh It looks fine to use Carbon features absent on Cocoa :) – eonil Jun 26 '11 at 07:04
3

In my case I wanted a presented NSViewController subclass to be able to listen to arrow key events for navigation with minimal effort. Here's the best solution I've found, a slight variation of Josh Caswell's answer.

Define an event monitor (optional), can be locally in your NSViewController subclass .m

id keyMonitor;

Then start monitoring events, for example in viewDidLoad.

keyMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) {

    unichar character = [[event characters] characterAtIndex:0];
    switch (character) {
        case NSUpArrowFunctionKey:
            NSLog(@"Up");
            break;
        case NSDownArrowFunctionKey:
            NSLog(@"Down");
            break;
        case NSLeftArrowFunctionKey:
            NSLog(@"Left");
            break;
        case NSRightArrowFunctionKey:
            NSLog(@"Right");
            break;
        default:
            break;
    }
    return event;
}];

To remove the monitor when not required (assuming you defined it)

[NSEvent removeMonitor:keyMonitor];
Daniel Nordh
  • 600
  • 6
  • 9
  • This is a very useful API for some edge cases where the responder chain doesn't do what you want. – w0mbat Jun 25 '20 at 03:33