如何检测在 Cocoa 中单击了哪个 NSTextField?

How to detect which NSTextField is clicked in Cocoa?

我有两个 NSTextField 对象,我想在用户点击它时突出显示它们。

初始文本字段已在 NSWindow 加载时突出显示。我能够获得文本字段单击的鼠标按下事件,但无法区分用户点击了哪个文本字段。

我尝试使用从 NSEvent 对象获得的 NSPoint 在文本字段上使用 hitTest,但返回的 NSView 为零。 returns 的视图是 window 的视图,而不是那个文本字段。

class SettingsViewController: NSViewController {
    private var sview: SettingsView?

    override func viewDidLoad() {
        initEvents()
    }

    override func loadView() {
        if let settingsView = SettingsView.createFromNib() {
            self.view = settingsView
            self.sview = settingsView as? SettingsView
        }
    }

    func initEvents() {
        self.sview!.emailTextField.delegate = self
    }

}

extension SettingsViewController: NSTextFieldDelegate, NSTextDelegate {
    override func mouseDown(with event: NSEvent) {
        self.log.debug("mouse down: \(event.buttonNumber), \(event.eventNumber), \(event.locationInWindow)")
        // How to know which text field triggered this?
    }

    func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
        self.log.debug("control delegate")
        return false
    }

    func textField(_ textField: NSTextField, textView: NSTextView, shouldSelectCandidateAt index: Int) -> Bool {
        self.log.debug("text field should select")
        return true
    }

    func textShouldBeginEditing(_ textObject: NSText) -> Bool {
        self.log.debug("text field should being editing")
        return true
    }
}

class SettingsView: NSView {
    private let log = Logger()
    private static var topLevelObjects: NSArray?
    @IBOutlet weak var emailTextField: ASTextField!
    @IBOutlet weak var passwordTextField: NSSecureTextField!

    // ...
}

我只向一个文本字段添加委托。

self.sview!.emailTextField.delegate = self

但是当我点击 passwordTextField 时,我也得到了鼠标点击事件。为什么会这样?

如何区分NSTextField鼠标点击和高亮文本域?


我尝试子类化 NSTextField 并添加点击处理程序,但它不起作用。

class ASTextField: NSTextField {
    private let log = Logger()

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        bootstrap()
    }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        bootstrap()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        bootstrap()
    }

    func bootstrap() {
        self.delegate = self
    }
}

extension ASTextField: NSTextFieldDelegate {
    override func mouseDown(with event: NSEvent) {
        // This is not working
        self.log.debug("mouse down")
        super.mouseDown(with: event)
    }
}

您覆盖的方法 mouseDown(with) 不是 NSTextFieldDelegateNSTextDelegate 协议的成员。您覆盖了 NSViewController.mouseDown(with).

每当该方法被调用时,被点击的东西就是您 SettingsViewController 的视图。

要对您的文本字段被选中做出反应,您可以使用您已有的 NSTextFieldDelegate .textField(_:textView:shouldSelectCandidateAt:)textView 参数的值是选择的文本视图。

我更新了 ASTextField 如下。

class ASTextField: NSTextField {

    // ...

    override func mouseDown(with event: NSEvent) {
        self.sendAction(#selector(didClick(_:)), to: self)
        super.mouseDown(with: event)
    }

    @objc func didClick(_ event: NSEvent) {
        self.log.debug("did click")
    }
}

SettingsView 中,我错过了调用 super.layout(),没有它点击将不起作用,点击时其他文本字段也不会获得焦点。

class SettingsView: NSView {
    // ...

    override func layout() {
        self.log.debug("layout method")
        super.layout()  // This is important
    }
}

NSTextField 不需要委托方法。

如果您想要的是在单击(聚焦)文本字段时能够 select 文本,您可以覆盖 class 以简化您的任务并且您赢了不必担心从委托中定位单击的字段。

对于 NSView 对象,当它获得焦点(即单击或 Tab 键)时,它会调用 becomeFirstResponder 以便我们可以挂钩。

当 NSTextField 变为可编辑(或 select 可编辑)时,它会抓取一个可重复使用的 'field editor' 并在编辑期间将其覆盖在您的文本字段之上。如果您的 NSTextField 有焦点,您可以使用视图上的 currentEditor() 调用来获取此字段编辑器。

因此,一旦您有了字段编辑器,就可以在编辑器上对 select 文本执行 selectAll

示例class:-

class AutoselectOnFocusTextField: NSTextField {
    override func becomeFirstResponder() -> Bool {
        guard super.becomeFirstResponder() else {
            return false
        }
        if let editor = self.currentEditor() {
            editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
        }
        return true
    }
}

希望对您有所帮助!