I'm going out of a limb here, since I would like to suggest a different approach which completely avoids "busy waiting" or "run loop waiting".
If I understood the camera correctly, it may take a certain duration until after the exposure point has been set by the camera. There is the property adjustingFocus
which reflects this state of the camera. This property is KVO compliant and we can use KVO to observe its value.
So, the idea is to set the exposure point, and then observe the property adjustingFocus
. When it's value changes to NO
, the camera is finished setting the exposure point.
Now, we can leverage KVO to call a completion hander immediately after the setting is complete. Your method to setup the exposure point becomes asynchronous with a completion handler:
typedef void (^completion_t) ();
-(void)continuousExposeAtPoint:(CGPoint)point
completion:(completion_t)completionHandler;
Assuming you have properly implemented KVO in the method above you can use it as follows:
-(void)captureMultipleImg
{
[self continuousExposeAtPoint:CGPointMake(0.0f, 0.0f) completion:^{
[self captureStillImage];
[self continuousExposeAtPoint:CGPointMake(0.5f, 0.5f) completion:^{
[self captureStillImage];
}];
}];
}
Edit:
Now, method captureMultipleImg
became asynchronous as well.
Note:
A method invoking an asynchronous method becomes itself asynchronous.
Thus, in order to let the call-site know when its underlying asynchronous task is finished, we may provide a completion handler:
typedef void (^completion_t)();
-(void)captureMultipleImagesWithCompletion:(completion_t)completionHandler
{
[self continuousExposeAtPoint:CGPointMake(0.0f, 0.0f) completion:^{
[self captureStillImage];
[self continuousExposeAtPoint:CGPointMake(0.5f, 0.5f) completion:^{
[self captureStillImage];
if (completionHandler) {
completionHandler();
}
}];
}];
}
A button action may be implemented as follows:
- (void)captureImages {
[self showLabel];
self.captureImagesButton.enabled = NO;
[manager captureMultipleImagesWithCompletion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self hideLabel];
self.captureImagesButton.enabled = NO;
});
}];
}
Edit:
For a jump start, you may implement the KVO and your method as shown below. Caution: not tested!
-(void)continuousExposeAtPoint:(CGPoint)point
completion:(completion_t)completionHandler
{
AVCaptureDevice* device; // ...;
if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
if([device lockForConfiguration:NULL]){
[device addObserver:self forKeyPath:@"adjustingExposure"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:(__bridge_retained void*)([completionHandler copy])];
[device setExposurePointOfInterest:point];
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change
context:(void *)context
{
AVCaptureDevice* device; // = ...;
if ([keyPath isEqual:@"adjustingExposure"]) {
if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue] == NO) {
CGPoint point = device.exposurePointOfInterest;
NSLog(@"Exposure point of intereset has been set to (%f,%f)",point.x, point.y);
[device removeObserver:self forKeyPath:@"adjustingExposure"];
[device unlockForConfiguration];
completion_t block = CFBridgingRelease(context);
if (block) {
block();
}
}
}
// Be sure to call the superclass's implementation *if it implements it.
// NSObject does not implement the method.
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
The caveat here is, that KVO is difficult to setup. But once you managed to wrap it into a method with a completion handler it looks much nicer ;)