简体   繁体   English

如何检测可可中单击哪个NSTextField?

[英]How to detect which NSTextField is clicked in Cocoa?

I have two NSTextField objects which I want to highlight when user clicks on it. 我有两个要在用户单击时突出显示的NSTextField对象。

The initial text field is already highlighted on NSWindow load. 初始文本字段已在NSWindow加载中突出显示。 I am able to get mouse down event for text field click, but unable to distinguish which textfield did the user tapped. 我能够获得鼠标按下事件来单击文本字段,但是无法区分用户点击了哪个文本字段。

I tried using hitTest on the text field using the NSPoint obtained from the NSEvent object, but the NSView returned is nil. 我尝试使用从NSEvent对象获得的NSPoint在文本字段上使用hitTest,但返回的NSView为nil。 The view it returns is that of the window's view and not that text field. 它返回的视图是窗口视图的视图,而不是文本字段。

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!

    // ...
}

I am adding delegate to only one text field. 我将委托添加到仅一个文本字段。

self.sview!.emailTextField.delegate = self

But when I click on the passwordTextField , I am getting the mouse click event as well. 但是,当我单击passwordTextField ,我也得到了鼠标单击事件。 Why is this happening? 为什么会这样呢?

How to distinguish NSTextField mouse click and highlight the text field? 如何区分NSTextField鼠标单击并突出显示文本字段?


I tried subclassing NSTextField and adding click handler, but it is not working. 我尝试将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)
    }
}

The method you overwrote, mouseDown(with) , isn't a member of the NSTextFieldDelegate or NSTextDelegate protocols. 您改写的方法mouseDown(with)不是NSTextFieldDelegateNSTextDelegate协议的成员。 You overwrote NSViewController.mouseDown(with) . NSViewController.mouseDown(with)

Whenever that method is called, the thing that was clicked is your SettingsViewController 's view. 每当调用该方法时,单击的对象就是SettingsViewController的视图。

To react to your textfield being selected, you use NSTextFieldDelegate .textField(_:textView:shouldSelectCandidateAt:) , which you already have. 要对您选择的文本字段做出反应,请使用已经具有的NSTextFieldDelegate .textField(_:textView:shouldSelectCandidateAt:) The value of the textView parameter is the text view that was selected. textView参数的值是所选的文本视图。

I updated the ASTextField as below. 我更新了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")
    }
}

In the SettingsView , I missed calling super.layout() , without which the click won't work, nor the other text field will get focus when clicked. SettingsView ,我错过了调用super.layout() ,如果没有该调用,单击将无法进行,单击时其他文本字段也不会获得焦点。

class SettingsView: NSView {
    // ...

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

NSTextField delegate methods are not required. 不需要NSTextField委托方法。

If what you're looking for is to be able to select the text when you click (focus) the text field, you can override the class to simplify your task and you won't have to worry about locating the clicked field from the delegate. 如果您要查找的是单击(聚焦)文本字段时能够选择文本,则可以重写该类以简化您的任务,而不必担心从委托中找到被单击的字段。

For an NSView object, when it gets focus (ie. clicking or tabbing) it will call becomeFirstResponder so we can hook in there. 对于一个NSView对象,当它获得焦点(即单击或制表)时,它将调用becomeFirstResponder以便我们可以将其挂接到那里。

When an NSTextField becomes editable (or selectable) it grabs a reusable 'field editor' and overlays it on top of your text field during the editing. 当NSTextField变为可编辑(或可选)时,它将获取可重用的“字段编辑器”,并在编辑期间将其覆盖在文本字段的顶部。 If your NSTextField has focus, you can grab this field editor using the currentEditor() call on the view. 如果您的NSTextField具有焦点,则可以使用视图上的currentEditor()调用获取此字段编辑器。

So, once you have the field editor, you can perform selectAll on the editor to select the text. 因此,一旦有了字段编辑器,就可以在编辑器上执行selectAll以选择文本。

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

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM