【问题标题】:How to detect which NSTextField is clicked in Cocoa?如何检测 Cocoa 中点击了哪个 NSTextField?
【发布时间】:2019-08-10 18:36:40
【问题描述】:

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

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

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

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

【问题讨论】:

标签: swift macos cocoa nstextfield


【解决方案1】:

如果您正在寻找的是能够在单击(聚焦)文本字段时选择文本,您可以覆盖该类以简化您的任务,并且您不必担心定位单击的字段来自代表。

对于一个 NSView 对象,当它获得焦点时(即单击或切换),它将调用becomeFirstResponder,因此我们可以在那里挂钩。

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

所以,一旦有了字段编辑器,就可以在编辑器上执行selectAll 来选择文本。

示例类:-

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

希望这会有所帮助!

【讨论】:

    【解决方案2】:

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

    每当调用该方法时,点击的内容就是您的SettingsViewController 的视图。

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

    【讨论】:

    • 啊!这就解释了为什么整个视图都会响应点击事件。那么如何检测文本字段点击呢?
    • @jsloop 我认为你刚刚提出了一个最好的例子,说明为什么你不应该使用委托接口来查看控制器,并为它们创建新的独立类
    • 委托没有被调用。我在github.com/jsloop42/purewind/tree/master/TestMacUI 创建了一个示例项目。你能看一下吗?
    • 为什么在目标 C 中?
    • 没有具体原因。我正在开发一个同时包含 Objective-C 和 Swift 的项目,并且我更喜欢 Objective-C :) 我已经在示例中添加了一个 Swift 目标。
    【解决方案3】:

    我将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 委托方法不是必需的。

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 2016-08-18
      • 1970-01-01
      • 2016-03-28
      相关资源
      最近更新 更多