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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With