Обработка событий клавиатуры в AppKit с помощью Swift

У меня есть пользовательский подкласс NSView, который должен обрабатывать некоторые события клавиатуры. В Objective-C я могу справиться с этим следующим образом:

-(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];
}

В Swift, однако, characterAtIndex: возвращает a unichar, а NSUpArrowFunctionKey: Int и " ": String (или Character). Мне не ясно, как преобразовать unichar в String или Character.

Я получил эту работу, но это похоже на уродливое обходное решение. Есть ли лучший способ?

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 a better way of doing this?
            // Scroll down one page
            break

        default:
            super.keyDown(theEvent)
    }
}

Ответ 1

Пусть часто пропускаемый interpretKeyEvents() сделает для вас беспорядочные биты. Он знает обо всех видах клавиш, включая клавиши со стрелками:

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

Раздел Ссылка на класс NSResponder Отвечая на сообщения о действиях, перечисляет эти и другие методы обработки событий клавиатуры.

Ответ 2

Почему бы не использовать расширения?

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

Также обратите внимание, что, как указано в этом ответе, не существует общедоступного перечисления, определяющего все коды клавиш для каждого ключа. Проще всего проверить, какое это значение, с помощью println(), а затем использовать это значение в операторе switch.


Изменить

Или вы также можете расширить класс Character

import Foundation
extension Character {

    var keyCode: Int {
        return Int(String(self).utf16[String.UTF16View.Index(0)])
    }

}

и проверить это следующим образом

case Character(" ").keyCode: // Spacebar
    println("spacebar!")

Ответ 3

OSX обрабатывает ключевые события, используя как минимум два разных уровня.

  • keyCode. Я считаю, что это код, сопоставленный с каждой клавишей аппаратной клавиатуры.
  • Unicode. Предопределенная кодовая точка Unicode, сопоставленная с семантическим виртуальным ключом.

Вам нужно использовать ключ Unicode для правильной обработки пользовательских событий. Вы можете взять кодовую точку Unicode из объекта события следующим образом.

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

Позаботьтесь о том, чтобы unicodeScalars не был доступен в произвольном порядке, поэтому вам нужно использовать явный индексный объект --- startIndex.