Reputation: 3751
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.
Upvotes: 44
Views: 27471
Reputation: 707
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")
}
Upvotes: -1
Reputation: 236538
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
}
}
Upvotes: 68
Reputation: 5219
I was trying to find an answer for swift 3, here is what worked for me:
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)
}
}
Upvotes: 10
Reputation: 219
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...
Upvotes: 21
Reputation: 1681
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
}
}
Upvotes: 2