44

I would like to capture keyevents in my little app.

What I have done:

class ViewController : NSViewController { ... override func keyDown(theEvent: NSEvent) { if theEvent.keyCode == 124 { println("abc") } else { println("abcd") } } override var acceptsFirstResponder: Bool { return true } override func becomeFirstResponder() -> Bool { return true } override func resignFirstResponder() -> Bool { return true } ... } 

What happens:

When a key pressed, the Funk sound effect plays.

I've seen many posts talking about how this is a delegate the belongs to NSView and NSViewController does not have access. But the keydown function override auto completes in a class of type NSViewController leading me to believe that this is wrong.

5
  • Which object is the first responder? Commented Sep 7, 2015 at 23:00
  • 2
    To eliminate the "Funk Sound" just return nil instead of the event, in the closure. I use the localMonitor approach. Commented Jul 27, 2016 at 12:00
  • 1
    One caveat muting the funk sound this way is that you don't forward the keyDown event to other parts of the app, like cmd+q, to mitigate this you could return nil only on keyDown events you want to target, if you want to target a,b,c keystrokes then return nil only on these keyStrokes etc. Commented Jul 27, 2016 at 12:21
  • the signature has changed on swift 4<br/>override func keyDown(with event: NSEvent) {}<br/> Commented Mar 19, 2019 at 20:33
  • it works fine but it drops support of built-in combinations like CMD+W to close the window or Ctrl+CMD+F to maximize the window Commented Jan 12, 2023 at 2:18

5 Answers 5

68

Xcode 8.2.1 • Swift 3.0.2

import Cocoa class 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 Cocoa class View: NSView { override func performKeyEquivalent(with event: NSEvent) -> Bool { return true } } 

Sample Project

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

13 Comments

How can you tell if you're using swift 1.2 or 2.0
This works in Swift 2.x - I can get the keyDown event in my override func keyDown() event code but I still get the "Funk" sound how do you eliminate the "Funk" sound?
Leo Dabus - are you suggesting I use your invisible button idea to eliminate the "Funk" sound for keyDown event?
My window (view) has several "Label" fields (Labels are really just NSTextFields).
|
20

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...

2 Comments

This is the only answer that helped me to get rid of the purr sound
Is there an easy way to detect long press (press and hold) of a key?
10

I was trying to find an answer for swift 3, here is what worked for me:

Swift 3

import Cocoa // We subclass an NSView class 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) } } 

Comments

2

I manage to get it work from subclass of NSWindowController

class MyWindowController: NSWindowController { override func keyDown(theEvent: NSEvent) { print("keyCode is \(theEvent.keyCode)") } } 

UPDATE:

import Cocoa protocol WindowControllerDelegate { func keyDown(aEvent: NSEvent) } class WindowController: NSWindowController { var delegate: WindowControllerDelegate? override func windowDidLoad() { super.windowDidLoad() delegate = window?.contentViewController as! ViewController } override func keyDown(theEvent: NSEvent) { delegate?.keyDown(theEvent) } } 

and ViewController:

class ViewController: NSViewController, WindowControllerDelegate { @IBOutlet weak var textField: NSTextField! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override var representedObject: AnyObject? { didSet { // Update the view, if already loaded. } } override func keyDown(theEvent: NSEvent) { textField.stringValue = "key = " + (theEvent.charactersIgnoringModifiers ?? "") textField.stringValue += "\ncharacter = " + (theEvent.characters ?? "") textField.stringValue += "\nmodifier = " + theEvent.modifierFlags.rawValue.description } } 

Comments

-1
 let kLeftArrowKeyCode: UInt16 = 123 let kRightArrowKeyCode: UInt16 = 124 let kDownArrowKeyCode: UInt16 = 125 let kUpArrowKeyCode: UInt16 = 126 override func keyDown(with event: NSEvent) { switch event.keyCode { case kLeftArrowKeyCode: print("left") break case kRightArrowKeyCode: print("right") break case kDownArrowKeyCode: print("down") break case kUpArrowKeyCode: print("up") break default: print("other") super.keyDown(with: event) break } print("Key with number: \(event.keyCode) was pressed") } 

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.