I have code that does different logic based on which camera is used such as:
if(aPickerInstance.cameraDevice == UIImagePickerControllerCameraDeviceFront){
// Front camera logic
}else{
// Rear camera logic
}
My issue is that when the switch occurs, front-to-rear or vice versa, I have found no way to tell when the "cameraDevice" property value has changed in order to reevaluate my states. (basically rerun logic blocks to set everything right for the newly selected camera).
When you create your picker, add an observer:
[_picker addObserver:self forKeyPath:#"cameraDevice" options:NSKeyValueObservingOptionNew context:nil];
Then add the handler:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"Keypath %# change %#", keyPath, change);
}
change[#"new"] will give you a new value for cameraDevice. You can also just use this to detect a change and trigger your own cameraDevice state check.
Related
Hi i am using the below code to display current location on the map. I notice that the current location does not seem to be updating when i travel around. I would like the location indicator to update the pointer according to the place i travel. How would i go about doing this? Currently only able to display the current location on the map and that is it.
[self.mapView.locationDisplay startDataSource];
self.mapView.locationDisplay.autoPanMode = AGSLocationDisplayAutoPanModeCompassNavigation;
You might Listen for location updates:
- (void)registerAsObserver {
[ self.mapView.locationDisplay addObserver:self
forKeyPath:#"location"
options:(NSKeyValueObservingOptionNew)
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqual:#"location"]) {
NSLog(#"Location updated to %#", [self.mapView.locationDisplay mapLocation]);
}
}
Try specifying wanderExtentFactor - add one more line of code, wanderExtentFactor can take values from 0 to 1 just play around to see which settings suit you best
[self.mapView.locationDisplay startDataSource];
self.mapView.locationDisplay.autoPanMode = AGSLocationDisplayAutoPanModeCompassNavigation;
self.mapView.locationDisplay.wanderExtentFactor = 0.5;
I've recently begun to discover what can be done with KVO and I'm refactoring some of my code and saving a lot of lines at the same time. I do face one issue that is so general that it makes me wonder whether a certain pattern is recommended.
In some cases I load a new view controller that needs to represent data from an already initialized model. On -viewDidLoad I would register for KVO:
[_model addObserver:self
forKeyPath:kSomeKey
options:NSKeyValueObservingOptionNew
context:(__bridge void *)(_model)];
and change my interface when values change:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqual:kSomeKey] && context == (__bridge void *)(_model)) {
[self updateSomeInterfaceElement];
}
Unfortunately and understandably, the view is not updated with current values from the model when I load my new view.
Is my best option to call -updateSomeInterfaceElement in -viewDidLoad? It doesn't seem to be a big deal like this, but when listening for 10-20 properties, it looks very inefficient (especially since all my -updateSomeInterfaceElement methods are mostly 1 line only, so no need to make them into a separate method). Is there any way to circumvent this, or is there a more elegant solution?
You want to change your options to include NSKeyValueObservingOptionInitial. This will cause KVO to fire a notification when you add the observer, providing the observer with the "initial" value.
Also, as an aside, you should get in the habit of calling super if observeValueForKeyPath:... is called for a notification you didn't sign up for. Also, it's a bit more bulletproof to avoid using "live" pointers in the role of KVO contexts (since a future object could have the same pointer if the current object is deallocated.) I generally prefer to use a pattern like this:
static void * const MyObservationContext = (void*)&MyObservationContext;
- (void)viewDidLoad
{
// ... other stuff ...
[self addObserver:self forKeyPath:#"model.someKey" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:MyObservationContext];
// ... other stuff ...
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == MyObservationContext)
{
// Do stuff
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
I've got a set of UITextFields which are enabled or disabled based on an if function (basically only allows 2 to be filled in, then the others are disabled).
I've got 2 methods to fade the background of the UItextFieldFade in and Fade out.
I want the animation to run every time the enabled property of the UITextField changes but don't really know how to track the actual change.
Any help would be great.
Thanks
Add an observer to the text field:
[_textField addObserver: self forKeyPath: #"enabled" options: NSKeyValueObservingOptionNew context:NULL];
You should implement this method:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *) context;
Where you handle the value change of the "enabled" property.
Documentation: Key value observing.
I have a view controller with a view that changes (for example), and I would like to observe the frame of any view that self.view is set to.
Is there any difference between:
[self.view addObserver:self forKeyPath:#"frame" options:0 context:nil];
and
[self addObserver:self forKeyPath:#"view.frame" options:0 context:nil];
For the second one, if the view changes will messages still be recieved when the new view's frame changes, or will it only send messages if the frame of the view that was set when the observer was added?
Is there any way to observe changes to the frame property even if the view of the view controller changes after adding the observer?
Use the second path. #"view.frame" will notify you about the frame changes even when the "view" itself is changed. Cocoa will add observers for every object in the keyPath "chain" for you automatically (which means every item in the keyPath must be KVO-compatible).
You asked if there is a difference between the two, The answer is yes, there is a difference between them:
The first one
says "me as a view", I add an observer named self (aka) viewControllerObject, if you invoked this in viewController.m whenever my property named "frame" is changed.
The Second one
Says "me as ViewController" I'm adding myselfAsAnObserver whenever theKeyPath named "view.frame" is changed.
Since every observer should implement
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
For this case you won't notice much difference because you added a viewController as an observer in either of the method above, but it will make a difference when you are dealing with different objects. But the rule is simple, each added observer should implement the
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
One more thing:
Its a good idea to create a context for observation
e.g
//In MyViewController.m
//..
static int observingViewFrameContext
// In ...
[self addObserver:self
forKeyPath:#"view.frame"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:&observingViewFrameContext];
// .. don' forget to remove an observer ! too
I followed the documents about how to set up and observer using KVO mechanism
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
It's suppose to be very easy.
I created an AVAudioPlayer object and I want to track after every change in it's current time.
I use this code to set up the observer:
[_player addObserver:self forKeyPath:#"currentTime" options:NSKeyValueObservingOptionNew context:NULL];
This code to handle the changes:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"currentTime"]) {
//Do something
}}
And this code when the audio ends:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
// Remove the observation
[_player removeObserver:self forKeyPath:#"currentTime"];}
For some reason the observer doesn't call to the -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
I know that I can use NSTImer and trigger it when the audio starts playing but I'm looking for smoother way to do this.
I also can use AVPlayer object instead and track it by using it's addPeriodicTimeObserverForInterval:queue:usingBlock:
But I don't want to lose all the advantages of the AVAudioPlayer object.
What am I doing wrong with the observer?
Do you have another suggestion how to use AVAudioPlayer and manage tracking after it's currentTime property?
Thanks in advance,
You're doing nothing wrong.
I've tried to do this as well and it just doesn't fire.
I had to use an NSTimer instead that polled the currentTime :(
KVO will fire for the currentTime property on AVAudioPlayer only when a caller changes the value directly. It will not fire when as the audio clip progresses. To track that, you will have to use NSTimer, as has already been suggested by deanWombourne.
Have you tried
//KVO [self setValue:[NSNumber numberWithFloat:currentTime] forKey:#"currentTime"];
I did this when currentTime changing,and the KVO work fine.but it's still need a timer to tell setting value =.=