2

I have many NSTextFields and I want to know, if the user has pressed one of the arrow keys while editing one of them. The function

override func keyDown(theEvent: NSEvent) {
    switch theEvent.character {
    case NSRightArrowFunctionKey:
        println(1)
        moveGor(NSRightArrowFunctionKey)
    case NSLeftArrowFunctionKey:
        moveGor(NSLeftArrowFunctionKey)
    case NSUpArrowFunctionKey:
        moveVert(NSUpArrowFunctionKey)
    case NSDownArrowFunctionKey:
        moveVert(NSDownArrowFunctionKey)
    default:
        super.keyDown(theEvent)
    }
}

doesn't seem to work. Is there any other way to do that in swift?

EDIT:

I have the extension for NSEvent:

extension NSEvent {
    var character: Int {
        let str = charactersIgnoringModifiers!.utf16
        return Int(str[str.startIndex])
    }
}

that I used in previous function

pomo_mondreganto
  • 2,028
  • 2
  • 28
  • 56

1 Answers1

5

When text fields have the focus, they actually don't. Instead, a text view is added to the window on top of the text field and that text view is the first responder and handles all of the input and editing behaviors. The text view is known as the "field editor". The text field does not receive key down events; the text view does.

You could substitute a custom text view as the first responder for the text field and have that text view handle the key down events specially. However, it's probably easier to take advantage of the fact that the text field is the delegate for the text view. Depending on exactly what you're trying to achieve, you might implement -textView:willChangeSelectionFromCharacterRange:toCharacterRange:, but that's not exclusively about arrow keys.

A more promising method might be -textView:doCommandBySelector:. That's also not really about the arrow keys, but in some ways it's better. The arrow keys, and all other standard editing keys, operate by being translated through the key bindings system into command selectors. The command selectors represent the semantic operation being performed, like -moveUp:. They are changed by modifier flags, so that Shift-up-arrow might generate -moveUpAndModifySelection:.

Anyway, in -textView:doCommandBySelector:, you can execute code based on the selector and either tell the text view not to do anything else (by returning YES) or let the text view do its normal thing in addition (by returning NO). (Obviously, return NO for anything that you don't care about.)

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • I've inherited from `NSTextField` and implemented `-textView:doCommandBySelector:` but it is not getting called. What am I missing here? – gerbil Dec 02 '17 at 14:46
  • Hard to know without specifics. I see you've asked your own question, but not provided any specifics there, either. Are you sure you're actually using an instance of your custom `NSTextField` subclass in your UI? – Ken Thomases Dec 02 '17 at 15:19
  • OK I solved it- One must set the `NSTextField`'s delegate in order to receive `-textView:doCommandBySelector:` calls. The confusing part is that the delegate of type `NSTextFieldDelegate` which does not implement `-textView:doCommandBySelector:`, rather - `NSControlTextEditingDelegate` does. You may want to add that to this answer. – gerbil Dec 03 '17 at 17:00
  • i.e. override: `func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool` – doug94028 Apr 05 '19 at 13:17