NSTextField delegate methods not getting called

I am trying to build a SwiftUI app in which a user can tab through several textfields laid out in a grid. The default behavior with the SwiftUI TextField does not work for me (I want the focus to proceed from top-to-bottom, rather than left-to-right) so I am trying to make a custom textfield by making NSTextField conform to NSViewRepresentable.

The code below displays in my application just fine, and the coordinator is initialized properly (it prints to the console), but none of the coordinator methods are ever called (they don't print anything). Am I doing something wrong? How do I fix this?

import SwiftUI

struct OrderedTextField: NSViewRepresentable {
  @Binding var text: String
  @Binding var selectedField: Int
   
  var tag: Int
   
  func makeNSView(context: NSViewRepresentableContext<OrderedTextField>) -> NSTextField {
    let textField = NSTextField()
    textField.delegate = context.coordinator
    textField.tag = tag
    textField.placeholderString = ""
    return textField
  }
   
  func makeCoordinator() -> Coordinator {
    Coordinator(text: $text)
  }
   
  func updateNSView(_ nsView: NSTextField, context: NSViewRepresentableContext<OrderedTextField>) {
    nsView.delegate = context.coordinator
    context.coordinator.newSelection = { newSelection in
      DispatchQueue.main.async {
        self.selectedField = newSelection
      }
    }
     
    if nsView.tag == self.selectedField {
      nsView.becomeFirstResponder()
    }
  }
}

extension OrderedTextField {
  class Coordinator: NSObject, NSTextFieldDelegate {
    @Binding var text: String
    var newSelection: (Int) -> () = { _ in }
     
    init(text: Binding<String>) {
      print("Initializing!")
      _text = text
    }
   
    func textShouldBeginEditing(_ textObject: NSText) -> Bool {
      print("Should begin editing!")
      return true
    }
     
    func textDidBeginEditing(_ notification: Notification) {
      print("Began editing")
    }
   
    func textDidChange(_ notification: Notification) {
      print("textDidChange")
    }
     
    func textShouldEndEditing(_ textObject: NSText) -> Bool {
      print("should end editing")
      return true
    }
     
    func textDidEndEditing(_ notification: Notification) {
      print("did end editing")
    }
  }
}
Answered by OOPer in 681736022

As far as I checked, your Coordinator does not have any methods defined in NSTextFieldDelegate. You should better try something like this:

extension OrderedTextField {
    class Coordinator: NSObject, NSTextFieldDelegate {
        @Binding var text: String
        var newSelection: (Int) -> () = { _ in }
        
        init(text: Binding<String>) {
            print("Initializing!")
            _text = text
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, candidatesForSelectedRange selectedRange: NSRange) -> [Any]? {
            print(#function)
            return nil
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, candidates: [NSTextCheckingResult], forSelectedRange selectedRange: NSRange) -> [NSTextCheckingResult] {
            print(#function)
            return candidates
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, shouldSelectCandidateAt index: Int) -> Bool {
            print(#function)
            return true
        }
        
        func controlTextDidBeginEditing(_ obj: Notification) {
            print(#function)
        }
        
        func controlTextDidEndEditing(_ obj: Notification) {
            print(#function)
        }
        
        func controlTextDidChange(_ obj: Notification) {
            print(#function)
        }
        
        func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool {
            print(#function)
            return true
        }
        
        func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
            print(#function)
            return true
        }
        
        //...
    }
}

Or you may try subclassing NSTextField and override methods like textShouldBeginEditing(_:), textDidBeginEditing(_:), textDidChange(_:) ... in the subclass.

Accepted Answer

As far as I checked, your Coordinator does not have any methods defined in NSTextFieldDelegate. You should better try something like this:

extension OrderedTextField {
    class Coordinator: NSObject, NSTextFieldDelegate {
        @Binding var text: String
        var newSelection: (Int) -> () = { _ in }
        
        init(text: Binding<String>) {
            print("Initializing!")
            _text = text
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, candidatesForSelectedRange selectedRange: NSRange) -> [Any]? {
            print(#function)
            return nil
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, candidates: [NSTextCheckingResult], forSelectedRange selectedRange: NSRange) -> [NSTextCheckingResult] {
            print(#function)
            return candidates
        }
        
        func textField(_ textField: NSTextField, textView: NSTextView, shouldSelectCandidateAt index: Int) -> Bool {
            print(#function)
            return true
        }
        
        func controlTextDidBeginEditing(_ obj: Notification) {
            print(#function)
        }
        
        func controlTextDidEndEditing(_ obj: Notification) {
            print(#function)
        }
        
        func controlTextDidChange(_ obj: Notification) {
            print(#function)
        }
        
        func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool {
            print(#function)
            return true
        }
        
        func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
            print(#function)
            return true
        }
        
        //...
    }
}

Or you may try subclassing NSTextField and override methods like textShouldBeginEditing(_:), textDidBeginEditing(_:), textDidChange(_:) ... in the subclass.

NSTextField delegate methods not getting called
 
 
Q