Coming from iOS to macOS, I wanted to read keyboard input in my macOS application. Not from a textfield but rather from the window itself. I thought this would be an easy task. After some trial and error, Googling, and then some StackOverflow fishing, I managed to get this to work. It wasn’t exactly straightforward for me to get but now that I did, I’d like to share the code that makes it work.
In my ViewController.swift file, I set up a few delegate callbacks
override var acceptsFirstResponder: Bool { return true } override func becomeFirstResponder() -> Bool { return true } override func resignFirstResponder() -> Bool { return true }
In my viewDidLoad, I added event monitoring (which eluded me for some time).
NSEvent.addLocalMonitorForEvents(matching: .keyUp) { (aEvent) -> NSEvent? in self.keyUp(with: aEvent) return aEvent } NSEvent.addLocalMonitorForEvents(matching: .keyDown) { (aEvent) -> NSEvent? in self.keyDown(with: aEvent) return aEvent }
I then added keyUp and keyDown functions for the NSViewController itself.
override func keyDown(with event: NSEvent) { if keyIsDown == true { return } keyIsDown = true if event.keyCode == 1 { print("s key pressed") } else if event.keyCode == 49 { print("spacebar pressed") } } override func keyUp(with event: NSEvent) { keyIsDown = false if event.keyCode == 1 { print("s key released") } else if event.keyCode == 49 { print("spacebar released") } }
I have a class variable (keyIsDown) which prevents keyboard repeats to flood the methods. Set that to false at the start.
This works a treat – and I didn’t have to subclass NSView, do anything in AppDelegate, etc. It’s clean, legible, and it does exactly what you’d expect it to do. This is for a single windowed application.
Sir, you are a god. I’ve been trying to get keystroke events working for days. Apple docs and StackOverflow are usually out of date, incomplete, or outright wrong, and trial and error coding is impossible when the storyboard and the viewController seem to be completely disconnected from each other. Thank you. Live long and prosper!
This worked for me, except I had to add a condition to return nil as the event in case I handled it in my callbacks. Otherwise would get the macOS default “key not recognized” bell sound.
Hi
Your code looks interesting but how do I detect the keydown for the ‘alt’ & ‘Cmd’ keys? What I am trying to do is when my app launches if those 2 keys are help down it boots into debug mode, is this possible. I am trying to replicate the ‘Repair’ option in Apples Photos.
Andrew
My comment has still not been moderated.
I am not exactly sure about capturing keys during launch (I imagine viewWillLoad or it’s equivalent might serve well enough).
https://www.w3.org/2002/09/tests/keys.html
Wow, so helpful. Worked like a charm. Thanks!
Works great, but if you open a second view controller and then you back to the first (where you are capturing key events), the key events are fired twice. Any solution??