11

I am trying to create an application for OS X using Swift and Cocoa. I want the application to respond to key events without the focus being on/in a text field. I created a new Cocoa project with storyboards in Xcode, and have modified the ViewController.swift class to the following:

import Cocoa class ViewController: NSViewController { override func mouseDown(theEvent: NSEvent) { println( "Mouse Clicked" ) } override func keyDown(theEvent: NSEvent) { println( "Key Pressed" ) } override func viewDidLoad() { super.viewDidLoad() } override var representedObject: AnyObject? { didSet { } } } 

When I run the program, I get console output when I click on the window, but I don't get any console output when I press any keys.

So my questions are: Why does my program respond to mouse events but not key events? What extra steps do I need to do for key events to work as the mouse events do?

It appears that my view controller is in the responder chain since it intercepts the mouse events, so that doesn't seem to be the problem. I have found other answers on here on SE saying I need to subclass the NSView or NSWindow class. Is it possible to get key events without doing that?

Thanks in advance.

EDIT: In addition to the accepted answer, Swift - Capture keydown from NSViewController is a great, clear solution.

3
  • could you please provide your code for subclassing NSView? I need to do the same thing I believe. I am trying to get arrow keys when pressed. Commented Jul 31, 2015 at 14:01
  • 1
    The best answer to this question is in here: stackoverflow.com/questions/32446978/… I am posting this because none of the examples given really helped me and I got a really simple answer in the post above that worked, even in Xcode 8 and Swift 3. Commented Aug 22, 2016 at 16:05
  • @jvarela That is a good link. Thanks. I'll update my question to link to it. Commented Aug 24, 2016 at 4:22

2 Answers 2

12

The difference you observed originates from the fact that Cocoa handles mouse events and keyboard events somewhat differently. In early stage of the event dispatching process, all events are sent to the NSWindow object by system. For mouse events, NSWindow forwards them to the view on which user clicked; for keyboard events, NSWindow will forward them to the first responder of current key window.

The initial first responder of an NSWindow is the window itself. When user clicks on an NSView, NSWindow will try to make that view the first responder by sending an acceptsFirstResponder message. If it returns YES, the clicked view will become first responder.

The "gotcha," however, is that acceptsFirstResponder returns NO by default. As a plain NSView won't become the first responder, it won't receive keyboard events. So the easiest solution is to subclass your NSView, override acceptsFirstResponder to return YES.

Sign up to request clarification or add additional context in comments.

Comments

0

Renfei Song's answer helped me out. I ended up using the obj-c extension mechanism to add a acceptsFirstResponder method to MTKView without subclassing MTKView. I know Swift supports a similar mechanism.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.