Generic solution (work with or without bindings) One way of dealing with this is based on the response here
Basically you use the controlTextDidChange(notification:) delegate method of NSTextField and you implement your validation code in it.
override func controlTextDidChange (notification: NSNotification) {
guard let textField = notification.object as? NSTextField else { return }
// test here, replace the dummy test below with something useful
if textField.stringValue != "expected value" {
myTextFieldOutlet.backgroundColor = NSColor.red
myErrorLabelOutlet.stringValue = "Error !!!"
} else {
// everything OK, reset the background color and error label to the normal state
....
}
}
Obviously myTextFieldOutlet is an outlet linked to your text field and myErrorLabelOutlet is an outlet to a conveniently placed label used to show errors (blank if no error should be presented)
Bindings oriented solution Be sure Validates immediately is selected in Interface Builder and implement the following method in the class where the binding is made (Tuning View Controller in your example)
override func validateValue(_ ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey inKey: String) throws {
// test here, replace the dummy test below with something useful
if roll_rate > 10.0 {
throw NSError(domain: "your-domain", code: 100, userInfo: [NSLocalizedDescriptionKey: "Error, roll rate too high"])
}
}
When the error is thrown, the user will be presented with the standard sheet announcing the error and the option to cancel the change or correct it.
If Continuously updates value is selected in Interface Builder the method above will be called for each keystroke in the text field, otherwise only after pressing Enter or loosing focus.
Note: For a full understanding on how updating values through bindings work, including what Validates immediately does, see the docs here.