Handling keyboard events in AppKit with Swift

BJ Homer picture BJ Homer · Jul 21, 2014 · Viewed 10.2k times · Source

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)
    }
}

Answer

Adam Preble picture Adam Preble · May 9, 2015

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.