ControlTextDidChange does not work to set NSTextField string

I am trying to find a method that tracks NSTextField text for changes. I tried the delegate method -(void)controlTextDidChange:(NSNotification *)obj , but it only works when the user enters text into the text box. If a text field string is programmatically set, for example, using a button, controlTextDidChange does not work.

Is there a method or other approach that I can use to control the contents of an NSTextField for changes?

My ButtonText class (defined as a delegate for 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 

XIB shows buttons and text field: enter image description here

0
source share
2 answers

One approach is to use KVO. In particular, add an instance of ButtonText as an observer for buttonField stringValue .

In more detail in your ButtonText file, once @property IBOutlet buttonField been set (i.e. if ButtonText is a subclass of NSWindowController , in -windowDidLoad , and if ButtonText is an NSViewController subclass in -loadView ), call

 [self.buttonField addObserver:self forKeyPath:@"stringValue" options:0 context:&ButtonTextKVOContext]; 

Define ButtonTextKVOContext earlier 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 not a subclass of NSWindowController or NSViewController , we will use a slightly different approach. As before, we will want to start the observation "after installing @property IBOutlet buttonField ". To do this, synthesize the buttonField property as the mButtonField member mButtonField that mButtonField

 @synthesize buttonField = mButtonField; 

and override the buttonField 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 is released, 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 layout, we should never set the mButtonField variable outside the -setButtonField: method. (This is not entirely true, but if we install mButtonField , we must first stop observing its start key @"stringValue" and start observing its new value @ "stringValue". Than just calling -setButtonField: most likely just will cause the code to repeat and not worth it.)

For help, check out the Apple documentation in the NSKeyValueObserving protocol .

+1
source

If your goal is to use bindings, you can override the setter method for the property that you bound to the text field and do whatever monitoring you want to do. So, for example, you have a text field whose value is bound to the myText property, then you can do something like this:

 -(void)setMyText:(NSString *) newValue { _myText= newValue; // do monitoring here } 

This should be called at any time when the user either enters a text field or you change the value in the code if you do this through a property and not by directly accessing ivar.

0
source

All Articles