Swift - Capture keydown from NSViewController
Xcode 8.2.1 • Swift 3.0.2
import Cocoaclass ViewController: NSViewController { @IBOutlet var textField: NSTextField! override func viewDidLoad() { super.viewDidLoad() NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { self.flagsChanged(with: $0) return $0 } NSEvent.addLocalMonitorForEvents(matching: .keyDown) { self.keyDown(with: $0) return $0 } } override func keyDown(with event: NSEvent) { switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) { case [.command] where event.characters == "l", [.command, .shift] where event.characters == "l": print("command-l or command-shift-l") default: break } textField.stringValue = "key = " + (event.charactersIgnoringModifiers ?? "") textField.stringValue += "\ncharacter = " + (event.characters ?? "") } override func flagsChanged(with event: NSEvent) { switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) { case [.shift]: print("shift key is pressed") case [.control]: print("control key is pressed") case [.option] : print("option key is pressed") case [.command]: print("Command key is pressed") case [.control, .shift]: print("control-shift keys are pressed") case [.option, .shift]: print("option-shift keys are pressed") case [.command, .shift]: print("command-shift keys are pressed") case [.control, .option]: print("control-option keys are pressed") case [.control, .command]: print("control-command keys are pressed") case [.option, .command]: print("option-command keys are pressed") case [.shift, .control, .option]: print("shift-control-option keys are pressed") case [.shift, .control, .command]: print("shift-control-command keys are pressed") case [.control, .option, .command]: print("control-option-command keys are pressed") case [.shift, .command, .option]: print("shift-command-option keys are pressed") case [.shift, .control, .option, .command]: print("shift-control-option-command keys are pressed") default: print("no modifier keys are pressed") } }}
To get rid of the purr sound when pressing the character keys you need to subclass your view, override the method performKeyEquivalent and return true.
import Cocoaclass View: NSView { override func performKeyEquivalent(with event: NSEvent) -> Bool { return true }}
Swift4
Just found a solution for the very same problem, Swift4. The idea behind that: if the pressed key was handled by a custom logic, the handler shall return nil, otherwise the (unhandled) event...
class MyViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() // ... NSEvent.addLocalMonitorForEvents(matching: .keyDown) { if self.myKeyDown(with: $0) { return nil } else { return $0 } } } func myKeyDown(with event: NSEvent) -> Bool { // handle keyDown only if current window has focus, i.e. is keyWindow guard let locWindow = self.view.window, NSApplication.shared.keyWindow === locWindow else { return false } switch Int( event.keyCode) { case kVK_Escape: // do what you want to do at "Escape" return true default: return false } }}
And here we are: no Purr / Funk sound when key is pressed...
[Update] Added check of keyWindow. Without this, keyDown() is fired even if another view/window contains the first responder...
I was trying to find an answer for swift 3, here is what worked for me:
Swift 3
import Cocoa// We subclass an NSViewclass MainView: NSView { // Allow view to receive keypress (remove the purr sound) override var acceptsFirstResponder : Bool { return true } // Override the NSView keydown func to read keycode of pressed key override func keyDown(with theEvent: NSEvent) { Swift.print(theEvent.keyCode) }}