I have the following key value observer method in a modal view:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"uploadComplete"]) {
NSLog(#"UploadVC hears upload complete");
[self dismissViewControllerAnimated:YES completion:nil];
}
}
I use this to watch a photo object and know when it is finished uploading. When I run this it behaves as expected, and the console logs "UploadVC hears upload complete" - but then the following line is not executed -- the modal does not get dismissed.
There are no errors or anything else, the view just sits there and the modal is never dismissed. What's going on here?
That may happen when you receive KVO notification on background thread and so attempts to update UI may result in any unexpected behaviour (UI not changed, changed after some delay, app crash etc etc). Make sure you call all updating UI code on main thread:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"uploadComplete"]) {
NSLog(#"UploadVC hears upload complete");
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
}
}
Related
I'm using a solution for here to make titleView clipsToBounds always true.
I have this in my ViewController and it works well, however, if I leave the ViewController by pressing the back button and then come back, it app crashes at the dispatch_async line.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if([object isEqual:[[self.navigationController.navigationBar subviews] objectAtIndex:2]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self.navigationController.navigationBar subviews] objectAtIndex:2].clipsToBounds = NO;
[self.navigationItem.titleView layoutIfNeeded];
});
}
}
Edit:
The only error I get is: Thread 1: EXC_BAD_ACCESS (code=1, address=0x102d8860)
The console doesn't provide any information other than (lldb)
You must removeObserver if you go out from viewController.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController.navigationBar.subviews[2]
removeObserver:self
forKeyPath:#"clipsToBounds"];
}
Check to your block
if (self.navigationController.navigationBar.subviews.count > 1){
…
I am writing a custom video player and here is my doubt:
Even though the value for the key "playbackLikelyToKeepUp" doesn't change,observer method is getting called.Here is the screenshot below:
How is this possible?
Here is the code:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
if([keyPath isEqualToString:#"playbackLikelyToKeepUp"]){
NSLog(#"%#",change[#"new"]);
if([change valueForKey:#"new"] == [NSNumber numberWithBool:YES])
[myPlayer play];
NSLog(#"%#",change);
}
}
and in the viewDidLoad method:
[myPlayerItem addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
and you can see the screenshot above.
I would like to observe a view controller that is presenting a view controller to determine when it stops presenting it.
I thought about trying KVO, which I'm not terribly familiar with:
[observedVC addObserver:self forKeyPath:NSStringFromSelector(#selector(presentedViewController)) options:0 context:NULL];
And then:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"Change: %#", keyPath);
}
But I am not seeing this get called. Is there a better way to go about this? I'm working on an SDK so I don't necessarily have any control over the observedVC.
Is observeValueForKeyPath always called from the main thread?
I'm logging calls with
-(void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(#"KVO: isMainThread %d", [NSThread isMainThread]);
// ...
}
and it seems to be printing 1 every time, but I was unable to find any guarantee of this in the docs. Can anyone confirm this is the case?
In general, no.
You receive observeValueForKeyPath:ofObject:change:context: on the thread which changed the value. The setter method that changes the value sends the message to all observers after updating the value and before returning.
If you only call the setter on the main thread, then you will only observe the change on the main thread.
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.