I had a property named myName in my class, like:
#property (nonatomic, strong) NSString *myName;
I need to send a notification when the myName property's value is changed.
Now I'm doing something like:
- (void)setMyName:(NSString *)name
{
_myName = name;
[[NSNotificationCenter defaultCenter] postNotificationName:CHANGE_NOTIFICATION object:nil];
}
I know there is something like Key-Value Observing in iOS. But I don't know how to implement it, I read the entire document, but couldn't get a good understanding.
Please help me to understand how to implement the same without using custom setter.
Try this:
MyClass *var = [MyClass new];
[var addObserver:self forKeyPath:#"myName" options:NSKeyValueChangeOldKey context:nil];
and implement
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
}
this method will be called anytime when myName property changes
In - (void)setMyName:(NSString *)name do this instead
[self willChangeValueForKey:#"myName"];
_myName = name;
[self didChangeValueForKey:#"myName"];
//this generates the KVO's
And where you want to listen (the viewController), there in viewDidLoad add this line:
[w addObserver:self forKeyPath:#"myName"
options:NSKeyValueObservingOptionNew context:nil];
//By doing this, you register the viewController for listening to KVO.
and also implement this method:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([[change objectForKey:NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
return;
} else {
//read the change dictionary, and have fun :)
}
}
//this method is invoked, whenever the property's value is changed.
To do this without the customer setter, just synthesize the property setter. This will create all the supporting calls to willChangeValueForKey / didChangeValueForKey.
#synthesize myName;
Then set property values with dot-syntax:
self.myName = #"Inigo Montoya"
Then the observers will receive the KVO notification automatically.
(You will need to remove the observer before you release the observed object.)
Related
I've looked at some other SO answers in regards to this and I thought I was implementing my code correctly but I am not getting results.
I have a mutable array property - arrLocations. In my .m file, in viewDidLoad I set up an observer for it and then add an item:
self.arrLocations = [[NSMutableArray alloc] init];
//add an observer to know when geocoding loops are updated
[self addObserver:self forKeyPath:#"arrLocations" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
[self insertObject:#"test" inArrLocationsAtIndex:0];
and then I have the KVO method:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"arrLocations"]) {
NSLog(#"Showing contents of self.arrLocations\n%#", self.arrLocations);
}
}
But the observer method never gets called.
The observer never gets called because the pointer to your array stays the same when you change the contents of your array.
You would have to add an observer to the array itself and observe a key of the array. Something like count. But you cannot do that because NSMutableArray is not KVO compliant.
So, to make this work you have to find another way. My first idea would be to create a wrapper class for NSMutableArray that fires a notification each time you add or remove items to your array.
#import "USST_Test.h"
#implementation USST_Test
-(void) observeValueForKeyPath:(NSString *)KeyPath ofObject:(id)object change:(NSDictionary *)
change context:(void *) context{
NSLog(#"yes i have been changed",nil);
NSLog(#"%#",[change objectForKey:#"new"]);
}
-(void)setFirstName:(NSString *)firstName{
[self addObserver:self forKeyPath:#"firstName" options:NSKeyValueObservingOptionNew context:nil];
}
#end
Here is my code and I always get NSNull printed.
I am new to Objective-c and any help is appreciated.
I'm not sure that you understand the purpose of KVO. You're having an object observe itself every time the first name is changed. Typically, another object would add an observer once, using a call like this:
[object addObserver:self forKeyPath:#"firstName" options:NSKeyValueObservingOptionNew context:nil];
This means that self would like to be informed when object changes its firstName key. Calling [object setFirstName:name] will automatically trigger the KVO update, so no extra code is needed in it.
You don't need to implement custom setter for this.
Somewhere when you init the UUST_Test class call
[self addObserver:self forKeyPath:#"firstName" options:NSKeyValueObservingOptionNew context:nil];
And remove
-(void)setFirstName:(NSString *)firstName{
[self addObserver:self forKeyPath:#"firstName" options:NSKeyValueObservingOptionNew context:nil];
}
But be careful to remove the observer when the UUST_Test object will be destroyed. For that you can implement -(void)dealloc method but do NOT call [super dealloc] if you are using ARC which I think you use.
I would like to add an observer on the property of an object.
I use this method :
[self addObserver:self forKeyPath:#"showButtons" options:NSKeyValueObservingOptionNew context:NULL];
When the observer and observed object is the same object the method observeValueForKeyPath i called, but when the observer is another object, the method is not called.
I explain what I have done.
I have an object ViewManager which has a property aString.
The class AppContainer has a property ViewManager.
In my class AppContainer, after the ViewManager property initilization, I add AppContainer as observer of ViewManager's aString property like this :
[self.viewManager addObserver:self forKeyPath:#"aString" options:NSKeyValueObservingOptionNew context:NULL];
In the AppContainer class, I have this method which is never called when the aString property changes.
Here is the never called method :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"has changed");
}
However I have added an observer in the ViewManager class itself like this :
[self addObserver:self forKeyPath:#"aString" options:NSKeyValueObservingOptionNew context:NULL];
and the called method in this class is :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"itself");
}
when the observer and abserved object are the same, it works.
Could I have some help ?
In the AppContainer class, I have this method which is never called when the aString property changes.
Maybe it's because you call self.viewManager addObserver when it's nil?
I am trying to add observer (KVO) to observe my custom cell. Once the cell is selected I should receive a notification of the event.
My code:
[colMain addObserver:self forKeyPath:#"colMain" options:0 context:NULL];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (keyPath == #"colMain") {
NSLog(#"cell Selected");
[self performSelector:#selector(deleteCell) withObject:nil];
}
}
colMain stands for collectionView. I am not quite sure how to do it cause I don't have customCell as a property otherwise it does not compile.
Any ideas?
Why not just set a delegate on your collection view and then implement one of these two methods?
[– collectionView:shouldSelectItemAtIndexPath:]
[– collectionView:didSelectItemAtIndexPath:]
I'm trying to observe changes to an NSMutableString isDetailView:
-(void)viewDidLoad {
[self addObserver:self forKeyPath:#"isDetailView" options:NSKeyValueObservingOptionNew context:nil];
[isDetailView setString:#"YES"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"obersedValueFOrKeyPath:%#", keyPath);
}
But the observeValueForKeyPath method never gets called. Any ideas?
You are not changing the property, only the content of the object it points to. If you make isDetailView a normal string and do
[self setIsDetailView: #"YES"]
it will work.
By the way, properties that start "is" are conventionally supposed to be boolean and that looks like a more appropriate type in this case too.