"Tap to focus" to "auto focus" when content in camera view changed. Logic like in the stock Camera app or the UIImagePickerController for iOS?

X.Y. picture X.Y. · May 30, 2013 · Viewed 7.7k times · Source

How do I handle the switch from "tap to focus" at a specific POI back to "auto focus" state automatically after lost focus when the contents in the view changed? If you notice the focus behavior in the stock Camera app or the UIImagePickerController, after you tap focus some area and you move the phone away, the camera can automatically switch to continuous auto focus at the center of the screen.

I need some more flexibility than what a UIImagePickerController can provide, so I need to use AVFoundation to mimic the UIImagePickerController behavior first...

Answer

X.Y. picture X.Y. · May 30, 2013

This sounds very complex at first to me... but it turned super simple, Apple already did the 99% of work for us. All you need to do is to set "subjectAreaChangeMonitoringEnabled" on and register KVO on "AVCaptureDeviceSubjectAreaDidChangeNotification"! On the iOS 6.1 docs:

The value of this property indicates whether the receiver should monitor the video subject area for changes, such as lighting changes, substantial movement, and so on. If subject area change monitoring is enabled, the capture device object sends an AVCaptureDeviceSubjectAreaDidChangeNotification whenever it detects a change to the subject area, at which time an interested client may wish to re-focus, adjust exposure, white balance, etc.

Before changing the value of this property, you must call lockForConfiguration: to acquire exclusive access to the device’s configuration properties. If you do not, setting the value of this property raises an exception. When you are done configuring the device, call unlockForConfiguration to release the lock and allow other devices to configure the settings.

You can observe changes to the value of this property using key-value observing.

(Even better, you don't need to handle many corner cases. What if the device is in the middle of "adjustingFocus" at a POI and the content changed? You don't want the device fall back to auto focus at the center, and want the focus action to finish. The "area did change notification" is only triggered after the focus is done.)

Some sample code snippet from my project. (The structure follows the official AVFoundation example AVCam, so you can put them in easily and try out):

// CameraCaptureManager.m

@property (nonatomic, strong) AVCaptureDevice *backFacingCamera;

- (id) init{
    self = [super init];
    if (self){

        // TODO: more of your setup code for AVFoundation capture session
        for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
            if (device.position == AVCaptureDevicePositionBack){
                self.backFacingCamera = device;
            }
        }

        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

        void (^subjectAreaDidChangeBlock)(NSNotification *) = ^(NSNotification *notification) {

            if (self.videoInput.device.focusMode == AVCaptureFocusModeLocked ){
                // All you need to do is set the continuous focus at the center. This is the same behavior as
                // in the stock Camera app
                [self continuousFocusAtPoint:CGPointMake(.5f, .5f)];
            }
        };

        self.subjectAreaDidChangeObserver = [notificationCenter addObserverForName:AVCaptureDeviceSubjectAreaDidChangeNotification
                                                                            object:nil
                                                                             queue:nil
                                                                        usingBlock:subjectAreaDidChangeBlock];

        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
        [self addObserver:self forKeyPath:keyPathAdjustingFocus options:NSKeyValueObservingOptionNew context:NULL];
    }

    return self;
}

-(void) dealloc{
    // Remove the observer when done
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter removeObserver:self.deviceOrientationDidChangeObserver];
}

- (BOOL) setupSession{
    BOOL sucess = NO;

    if ([self.backFacingCamera lockForConfiguration:nil]){
        // Turn on subject area change monitoring
        self.backFacingCamera.subjectAreaChangeMonitoringEnabled = YES;
    }

    [self.backFacingCamera unlockForConfiguration];

    // TODO: Setup add input etc...

    return sucess;
}