此应用程序的目的是确保用户已在NSTextField中输入了某些文本。如果该文本不在字段中,则不应允许他们离开该字段。
给出一个带有子类文本字段,一个按钮和另一个通用NSTextField的macOS应用。单击该按钮时,将显示一个NSPopover,它“附加”到一个由名为myPopoverVC的NSViewController控制的字段。
例如,用户在顶部字段中输入3,然后单击“显示弹出窗口”按钮,该按钮显示弹出窗口并提示:“ 1 + 1等于多少?”
请注意,此弹出窗口有一个标为1st resp的字段,因此当弹出窗口显示时,该字段将成为第一响应者。目前不会输入任何内容-仅用于此问题。
用户将单击“关闭”按钮,以关闭弹出窗口。届时,如果用户单击或跳开字段中带有“ 3”的选项卡,该应用程序将不允许该移动-可能会发出哔声或其他消息。但是,当弹出窗口关闭并且用户按下Tab键时,会发生什么情况
即使其中带有'3'的字段具有焦点环,它也应再次指示该窗口中的第一响应者,用户仍可以单击或远离它作为textShouldEndEditing函数不被调用。在这种情况下,我单击了弹出窗口中的关闭按钮,“ 3”字段具有聚焦环,然后单击选项卡,然后转到下一个字段。
这是子类别的文本字段中的函数,在将文本输入到该字段后,该函数可以正常工作。在这种情况下,如果用户键入3并按Tab键,则光标将停留在该字段中。
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
if let editor = self.currentEditor() { //or use the textObject
let s = editor.string
if s == "2" {
return true
}
return false
}
showPopover按钮代码将aboutToShowPopover标志设置为true,这将允许子类显示弹出窗口。 (当弹出窗口关闭时设置为false)
所以问题是当弹出框关闭时,如何将firstResponder状态返回到原始文本字段?它似乎具有第一响应者状态,并且认为它具有该状态,尽管未调用textShouldEndEditing。 如果在该字段中键入另一个字符,则一切正常。就像窗口的字段编辑器和其中带有“ 3”的字段断开连接一样,因此字段编辑器不会将调用传递给该字段。
按钮将调用一个包含以下内容的函数:
let contentSize = myPopoverVC.view.frame
theTextField.aboutToShowPopover = true
parentVC.present(myPopoverVC, asPopoverRelativeTo: contentSize, of: theTextField, preferredEdge: NSRectEdge.maxY, behavior: NSPopover.Behavior.applicationDefined)
NSApplication.shared.activate(ignoringOtherApps: true)
NSPopover关闭为
parentVC.dismiss(myPopoverVC)
另一条信息。我将这段代码添加到了子类化的NSTextField控件中。
override func becomeFirstResponder() -> Bool {
let e = self.currentEditor()
print(e)
return super.becomeFirstResponder()
}
当弹出窗口关闭并且textField成为Windows的第一响应者时,该代码将执行,但输出nil。这表明虽然它是第一个响应者,但它与窗口fieldEditor无关,并且不会接收事件。为什么?
如果不清楚,请询问。
答案 0 :(得分:2)
如果对每个NSTextField进行子类化,则可以覆盖方法 becomeFirstResponder ,并将其发送 self 到要创建的委托类,该委托类将保留对当前的第一响应者:
NSTextField超类:
override func becomeFirstResponder() -> Bool {
self.myRespondersDelegate.setCurrentResponder(self)
return super.becomeFirstResponder()
}
( myRespondersDelegate :可以选择是您的NSViewController)
注意 :不要将相同的超类用于警报TextField和ViewController TextField。将此父类与添加的功能一起使用,仅适用于您希望在警报关闭后返回至firstResponder的TextField。
NSTextField委托:
class MyViewController: NSViewController, MyFirstResponderDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
}
现在,关闭弹出窗口后,您可以在 viewWillAppear 中创建一个委托函数,该函数将在弹出窗口中弹出 didDismisss (取决于弹出窗口的方式)已实施,我将显示委托选项) 检查是否存在TextField,然后重新创建 firstResponder 。
弹出代理:
class MyViewController: NSViewController, MyFirstResponderDelegate, MyPopUpDismissDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
func didDismisssPopUp() {
guard let isLastTextField = self.currentFirstResponderTextField else {
return
}
self.isLastTextField?.window?.makeFirstResponder(self.isLastTextField)
}
}
希望它能起作用。
答案 1 :(得分:2)
这是我在How can one programatically begin a text editing session in a NSTextField?和How can I make my NSTextField NOT highlight its text when the application starts?的帮助下进行的尝试:
所选范围保存在textShouldEndEditing
中,并在becomeFirstResponder
中恢复。 insertText(_:replacementRange:)
开始进行编辑会话。
var savedSelectedRanges: [NSValue]?
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() {
if self.aboutToShowPopover {
if let ranges = self.savedSelectedRanges {
if let fieldEditor = self.currentEditor() as? NSTextView {
fieldEditor.insertText("", replacementRange: NSRange(location: 0, length:0))
fieldEditor.selectedRanges = ranges
}
}
}
return true
}
return false
}
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if super.textShouldEndEditing(textObject) {
if self.aboutToShowPopover {
let fieldEditor = textObject as! NSTextView
self.savedSelectedRanges = fieldEditor.selectedRanges
return true
}
let s = textObject.string
if s == "2" {
return true
}
}
return false
}
也许重命名aboutToShowPopover
。
答案 2 :(得分:0)
非常感谢Willeke的帮助和答案,这导致了一个非常简单的解决方案。
这里的大问题是,当弹出窗口关闭时,“焦点”字段是原始字段。但是,由于某种原因,似乎Windows字段编辑器委托与该字段断开了连接,因此诸如control:textShouldEndEditing之类的功能并未传递给问题中的子类字段。
当该字段成为第一响应者时执行此行似乎将Windows字段编辑器与此字段重新连接,以便它将接收委托消息
fieldEditor.insertText("", replacementRange: range)
所以最终的解决方案是以下两个功能的组合。
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
let s = textObject.string
if s == "2" {
return true
}
return false
}
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() == true {
if let myEditor = self.currentEditor() as? NSTextView {
let range = NSMakeRange(0, 0)
myEditor.insertText("", replacementRange: range)
}
return true
}
return false
}