Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift NSViewController responds to mouseDown event, but not keyDown event

Tags:

cocoa

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.

like image 677
Bernem Avatar asked Jun 09 '15 05:06

Bernem


1 Answers

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.

like image 136
Renfei Song Avatar answered Jan 04 '23 00:01

Renfei Song