问题
I'm trying to find a method that monitors the text of NSTextField for changes. I tried the delegate method of -(void)controlTextDidChange:(NSNotification *)obj
but it only works when the user types into the text field. If the text field string is programmatically set, such as with a button, the controlTextDidChange
doesn't work.
Is there a method or another approach that I can use to monitor the contents of a NSTextField for changes?
My ButtonText class (set as delegate for the NSTextField):
#import "ButtonText.h"
@interface ButtonText ()
@property (weak) IBOutlet NSTextField *buttonField;
@end
@implementation ButtonText
- (IBAction)buttonTextA:(id)sender {
[_buttonField setStringValue:@"text A here"];
}
- (IBAction)buttonTextB:(id)sender {
[_buttonField setStringValue:@"and text B stuff"];
}
- (void)controlTextDidChange:(NSNotification *)obj {
NSLog(@"controlTextDidChange: %@", _buttonField.stringValue);
}
@end
The XIB showing the buttons and text field:
回答1:
One approach is to use KVO. In particular, add the ButtonText
instance as an observer of buttonField
's stringValue
.
In more detail, in your file ButtonText
, once the @property IBOutlet buttonField
has been set (i.e. if ButtonText
is an NSWindowController
subclass, in -windowDidLoad
, and if ButtonText
is an NSViewController
subclass in -loadView
), call
[self.buttonField addObserver:self
forKeyPath:@"stringValue"
options:0
context:&ButtonTextKVOContext];
Define ButtonTextKVOContext
previously in the file as follows:
static int ButtonTextKVOContext = 0;
Then override observeValueForKeyPath:ofObject:change:context:
as follows:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context != &ButtonTextKVOContext) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
if (object == self.buttonField) {
if ([keyPath isEqualToString:@"stringValue"]) {
NSLog(@"controlTextDidChange: %@", _buttonField.stringValue);
}
}
}
Edit
Since ButtonText
is not a subclass of NSWindowController
or NSViewController
, we'll use a slightly different approach. As before, we'll want to start observing "once the @property IBOutlet buttonField
has been set". To do this, synthesize the property buttonField
to be the member variable mButtonField
writing
@synthesize buttonField = mButtonField;
and override buttonField
's setter as follows:
- (void)setButtonField:(NSTextField *)buttonField
{
[self stopObservingButtonField];
mButtonField = buttonField;
[self startObservingButtonField];
}
We need to make sure that ButtonText
stops observing the button field when it deallocates as well, so override -dealloc
as follows:
- (void)dealloc
{
[self stopObservingButtonField];
}
It remains to define the methods -stopObservingButtonField
and -startObservingButtonField
:
- (void)stopObservingButtonField
{
if (mButtonField) {
[mButtonField removeObserver:self
forKeyPath:@"stringValue"
context:&ButtonTextKVOContext];
}
}
- (void)startObservingButtonField
{
if (mButtonField) {
[self.buttonField addObserver:self
forKeyPath:@"stringValue"
options:0
context:&ButtonTextKVOContext];
}
}
As a result of this arrangement, we must never set the mButtonField
variable outside of the -setButtonField:
method. (This isn't quite true, but if we do set mButtonField
we must be sure to first of all stop observing its old value's @"stringValue"
key path and start observing its new value's @"stringValue" key path. Doing this rather than simply calling -setButtonField:
would very likely simply constitute code repetition and not be worthwhile.)
For reference, check out Apple's documentation on the NSKeyValueObserving protocol.
回答2:
If your goal is to use bindings, then you can override the setter method for the property you have bound to the text field's value, and do whatever monitoring you want to do there. So,for instance, you have a text field whose value is bound to the property, myText, then you could do something like this:
-(void)setMyText:(NSString *) newValue {
_myText= newValue;
// do monitoring here
}
This should be called any time the user either types in the text field or you change the value in code, as long as you do it through the property, and not by directly accessing the ivar.
来源:https://stackoverflow.com/questions/13130613/controltextdidchange-not-working-for-setting-string-of-nstextfield