I have a custom NSView subclass that needs to handle some keyboard events. In Objective-C, I might handle it like this:
-(void)keyDown:(NSEvent *)event
{
unichar ch = [[event charactersIgnoringModifiers] characterAtIndex:0];
if (ch == NSUpArrowFunctionKey && (event.modifierFlags & NSCommandKeyMask)) {
// Scroll to top
return;
}
else if (ch == NSDownArrowFunctionKey && (event.modifierFlags & NSCommandKeyMask)) {
// Scroll to bottom
return;
}
switch (ch) {
case NSRightArrowFunctionKey:
// Select the current row
return;
case ' ':
// Scroll down one page
return;
default:
break;
}
[super keyDown:event];
}
In Swift, however,characterAtIndex:
returns a unichar
, while NSUpArrowFunctionKey: Int
and " ": String
(or Character
). It's not clear to me how to convert a unichar to a String
or Character
.
I got this working, but it feels like an ugly workaround. Is there a better way?
func keyDown(theEvent: NSEvent) {
let char = Int(theEvent.charactersIgnoringModifiers.utf16[0]) // <----- This seems ugly
let hasCommand = (theEvent.modifierFlags & .CommandKeyMask).value != 0
switch char {
case NSUpArrowFunctionKey where hasCommand == true:
// Scroll to top
break
case NSDownArrowFunctionKey where hasCommand == true:
// Scroll to bottom
break
case NSRightArrowFunctionKey where hasCommand == true:
// Select the current row
break
case Int(" ".utf16[0]): // <---- Surely there's a better way of doing this?
// Scroll down one page
break
default:
super.keyDown(theEvent)
}
}
Let the oft-overlooked interpretKeyEvents()
do the messy bits for you. It knows about all sorts of keys, including arrow keys:
override func keyDown(event: NSEvent) {
interpretKeyEvents([event]) // calls insertText(_:), moveUp(_:), etc.
}
override func insertText(insertString: AnyObject) {
let str = insertString as! String
switch str {
case " ":
println("User hit the spacebar.")
default:
println("Unrecognized input: \(str)")
}
}
override func moveUp(sender: AnyObject?) {
println("Up arrow.")
}
override func moveLeft(sender: AnyObject?) {
println("Left arrow.")
}
override func deleteBackward(sender: AnyObject?) {
println("Delete.")
}
The NSResponder Class Reference section Responding to Action Messages lists these and other methods for handling keyboard events.
Why not use extensions?
extension NSEvent {
var character: Int {
// Note that you could also use Int(keyCode)
return Int(charactersIgnoringModifiers.utf16[0])
}
}
override func keyDown(theEvent: NSEvent!) {
switch theEvent.character {
case NSUpArrowFunctionKey:
println("up!")
case 0x20:
println("spacebar!")
default:
super.mouseDown(theEvent)
}
}
Also, note that, as stated in this answer, there is no public enum that defines all the key codes for every key. It's easiest to simply test what value it is with a println()
and then use that value in the switch statement.
Or you could also extend the Character class
import Foundation
extension Character {
var keyCode: Int {
return Int(String(self).utf16[String.UTF16View.Index(0)])
}
}
and test for it like this
case Character(" ").keyCode: // Spacebar
println("spacebar!")
OSX handles key-events using at least two different layers.
keyCode
. I believe this is a code mapped to each hardware keyboard key.You need to use Unicode key to process user events properly. You can take the Unicode code-point from the event object like this.
override func keyDown(theEvent: NSEvent) {
let s = theEvent.charactersIgnoringModifiers!
let s1 = s.unicodeScalars
let s2 = s1[s1.startIndex].value
let s3 = Int(s2)
switch s3 {
case NSUpArrowFunctionKey:
wc1.navigateUp()
return
case NSDownArrowFunctionKey:
wc1.navigateDown()
return
default:
break
}
super.keyDown(theEvent)
}
Take care that unicodeScalars
is not randomly accessible, so you need to use explicit index object --- startIndex
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With