16

Is there a way to change the responder or select another textfield by pressing tab on the keyboard, in Swift?

enter image description here

Notes: It's for a fill in the blank type application.

My VC creates a list of Words [Word], and each of those words has its own WordView - word.wordView. The WordView is what is displayed. WordView is a child of NSTextField.

I tried to override keydown but it doesn't allow me to type anything in the text view.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Aaron
  • 1,312
  • 3
  • 14
  • 25

4 Answers4

21

You have to connect your textField nextKeyView to the next textField through the IB or programmatically:

textField1.nextKeyView = textField2

enter image description here

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • Can't quite make this work, but it sounds like a great idea. My loop currently looks like this: previousWord.wordView.nextKeyView = word.wordView – Aaron Mar 24 '16 at 19:29
  • But it still just creates a tab space in the current text view, it does not change to the "nextKeyView" when I press Tab. – Aaron Mar 24 '16 at 19:30
  • If you post a sample project I can take a look to find out whats going on otherwise I would be just guessing. Have you checked the sample project I've posted? – Leo Dabus Mar 24 '16 at 19:31
  • It works as expected tab goes to the next and shift-tab goes to the previous – Leo Dabus Mar 24 '16 at 19:34
  • 1
    Found the problem. I am using NSTextView not NSTextField. NSTextView just inserts a tab, while NSTextField moves to the next TextField. Interesting. – Aaron Mar 24 '16 at 19:39
  • 1
    Thanks so much @Leo Dabus That was exactly the help I needed!! Been stumped on this problem for weeks. – Aaron Mar 24 '16 at 19:46
  • Do you know of a way to go to nextKeyView programmatically? Say, if they type the correct answer in the blank it will move on automatically to the next blank. – Aaron Mar 24 '16 at 20:15
  • 1
    @Aaron you can set the textField delegate and add textField method controlTextDidChange to monitor the user text input and decide where to go from there. You would probably need to store that info somewhere in memory or disk. – Leo Dabus Mar 24 '16 at 22:50
  • 1
    It works perfectly! Thanks again @Leo Dabus for all your help! The project file helped a lot too! – Aaron Mar 25 '16 at 01:15
5

Assuming you want to go from textView1 to textView2. First set the delegate:

self.textView1.delegate = self

Then implement the delegate method:

func textView(textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool {
    if commandSelector == "insertTab:" && textView == self.textView1 {
        self.window.makeFirstResponder(self.textView2)
        return true
    }
    return false
}
rocky
  • 3,521
  • 1
  • 23
  • 31
  • Help me understand what "self" is in this case? the View Controller? – Aaron Mar 18 '16 at 00:23
  • My VC creates a list of Words [Word], and each of those words has its own WordView - word.wordView. The WordView is what is displayed. WordView is a child of NSTextView. – Aaron Mar 18 '16 at 00:25
  • When I put the func in the VC it says: ViewController does not have member 'window' – Aaron Mar 18 '16 at 01:23
  • 'window' is the window that your view lives in. self is whatever it is that owns the NSTextViews. It is usually in a view controller but not necessarily. – rocky Mar 18 '16 at 04:43
  • Tried: self.window = self.view.window! It doesn't work, returns nil. Ideas on how to reference the VC's window? – Aaron Mar 18 '16 at 18:27
  • Your app delegate should have a reference to your NSWindow right? – rocky Mar 18 '16 at 18:31
  • Not that I can find. Sorry. I haven't touched app delegate. It is the OS X cocoa template file. But I can't find any window element in there except for windowWillReturnUndoManager function. – Aaron Mar 18 '16 at 18:38
  • Your application has a window right? Find out who owns the window and you can get a reference to it. I can't really help any further without access to your project. – rocky Mar 18 '16 at 18:41
  • If you're inside an `NSViewController` `self.view.window.makeFirstResponder(textView2)` should work – Max Phillips Jun 21 '16 at 20:48
  • Related question about this https://stackoverflow.com/questions/2484072/how-can-i-make-the-tab-key-move-focus-out-of-a-nstextview – Keith Smiley Jun 23 '17 at 04:18
1

I had the same or a similar problem, in that I wanted to use an NSTextView field, to allow multiple lines of text to be entered, but it was the sort of field where entering a tab character would make no sense. I found an easy fix for this: NSTextView has an instance property of isFieldEditor, which is set to false by default; simply set this to true, and tabs will now skip to the next field.

hbowie
  • 21
  • 1
0

If you want some control over how your field tabs or moves with arrow keys between fields in Swift, you can add this to your delegate along with some move meaningful code to do the actual moving like move next by finding the control on the superview visibly displayed below or just to the right of the control and can accept focus.

 public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
        switch commandSelector {
        case #selector(NSResponder.insertTab(_:)), #selector(NSResponder.moveDown(_:)):
            // Move to the next field
            Swift.print("Move next")
            return true
        case #selector(NSResponder.moveUp(_:)):
            // Move to the previous field
            Swift.print("Move previous")
            return true
        default:
            return false
        }
        return false // I didn't do anything
    }