The object of this app is to ensure the user has entered a certain text in an NSTextField. If that text is not in the field, they should not be allowed to leave the field.
Given a macOS app with a subclass text field, a button and another generic NSTextField. When the button is clicked, an NSPopover is shown which is 'attached' to the field which is controlled by an NSViewController called myPopoverVC.
For example, the user enters 3 in the top field and then clicks the Show Popover button which displays the popover and provides a hint: 'What does 1 + 1 equal'.
Note this popover has a field labelled 1st resp so when the popover shows, that field becomes the first responder. Nothing will be entered at this time - it's just for this question.
The user would click the Close button, which closes the popover. At that point what should happen if the user clicks or tabs away from the field with the '3' in it, the app should not permit that movement - perhaps emitting a Beep or some other message. But what happens when the popover closes and the user presses Tab
Even though that field with the '3' in it had a focus ring, which should indicate the first responder again in that window, the user can click or tab away from it as the textShouldEndEditing function is not called. In this case, I clicked the close button in the popover, the '3' field had a focus ring and I hit tab, which then went to the next field.
This is the function in the subclassed text field that works correctly after the text has been entered into the field. In this case, if the user types a 3 and then hits Tab, the cursor stays in that field.
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
}
The showPopover button code sets the aboutToShowPopover flag to true which will allow the subclass to show the popover. (set to false when the popover closes)
So the question is when the popover closes how to return the firstResponder status to the original text field? It appears to have first responder status, and it thinks it has that status although textShouldEndEditing is not called. If you type another char into the field, then everything works as it should. It's as if the window's field editor and the field with the '3' in it are disconnected so the field editor is not passing calls up to that field.
The button calls a function which contains this:
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)
the NSPopover close is
parentVC.dismiss(myPopoverVC)
One other piece of information. I added this bit of code to the subclassed NSTextField control.
override func becomeFirstResponder() -> Bool {
let e = self.currentEditor()
print(e)
return super.becomeFirstResponder()
}
When the popover closes and the textField becomes the windows first responder, that code executes but prints nil. Which indicates that while it is the first responder it has no connection to the window fieldEditor and will not receive events. Why?
If anything is unclear, please ask.
textShouldEndEditing
, usetextObject
instead ofself.currentEditor
. – Vermiculationself.window.makeFirstResponder(nil)
) and then change it back to the text field? – MultifarioustextShouldEndEditing
returnsfalse
? – VermiculationtextShouldEndEditing
is called. – Vermiculationself.currentEditor()
will return the field editor aftersuper.becomeFirstResponder()
. – Vermiculationoverride func becomeFirstResponder()
returns a bool, so there isn't a way to callself.currentEditor()
after the return in that function. Or do you mean calling that in another function that followsbecomeFirstResponder
? If so, where would you get the current editor? The other issue is that even if that returns the current field editor, it's not the windows field editor so therefore the events are not being passed from the window's field editor to it's delegate, which should be the fields editor. – Fatherhood