AVFoundation点击以聚焦反馈矩形

我正在开发一个iphone应用程序,我直接使用AVFoundation通过相机捕捉video。

我已经实现了一个function,使用户可以tap to focusfunction。

 - (void) focus:(CGPoint) aPoint; { #if HAS_AVFF Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice"); if (captureDeviceClass != nil) { AVCaptureDevice *device = [captureDeviceClass defaultDeviceWithMediaType:AVMediaTypeVideo]; if([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { CGRect screenRect = [[UIScreen mainScreen] bounds]; double screenWidth = screenRect.size.width; double screenHeight = screenRect.size.height; double focus_x = aPoint.x/screenWidth; double focus_y = aPoint.y/screenHeight; if([device lockForConfiguration:nil]) { [device setFocusPointOfInterest:CGPointMake(focus_x,focus_y)]; [device setFocusMode:AVCaptureFocusModeAutoFocus]; if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]){ [device setExposureMode:AVCaptureExposureModeAutoExpose]; } [device unlockForConfiguration]; } } } #endif } 

到目前为止这么好,但我缺less像照片应用程序中的反馈矩形。 有没有什么方法可以告诉AVFoundation框架显示这个反馈矩形,还是我必须自己实现这个function?

以下是我所做的:这是创build用户在相机覆盖层上轻触时显示的正方形的类。

 CameraFocusSquare.h #import <UIKit/UIKit.h> @interface CameraFocusSquare : UIView @end CameraFocusSquare.m #import "CameraFocusSquare.h" #import <QuartzCore/QuartzCore.h> const float squareLength = 80.0f; @implementation FBKCameraFocusSquare - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code [self setBackgroundColor:[UIColor clearColor]]; [self.layer setBorderWidth:2.0]; [self.layer setCornerRadius:4.0]; [self.layer setBorderColor:[UIColor whiteColor].CGColor]; CABasicAnimation* selectionAnimation = [CABasicAnimation animationWithKeyPath:@"borderColor"]; selectionAnimation.toValue = (id)[UIColor blueColor].CGColor; selectionAnimation.repeatCount = 8; [self.layer addAnimation:selectionAnimation forKey:@"selectionAnimation"]; } return self; } @end 

而在你收到你的水龙头的视图,请执行以下操作:

 - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [[event allTouches] anyObject]; CGPoint touchPoint = [touch locationInView:touch.view]; [self focus:touchPoint]; if (camFocus) { [camFocus removeFromSuperview]; } if ([[touch view] isKindOfClass:[FBKVideoRecorderView class]]) { camFocus = [[CameraFocusSquare alloc]initWithFrame:CGRectMake(touchPoint.x-40, touchPoint.y-40, 80, 80)]; [camFocus setBackgroundColor:[UIColor clearColor]]; [self addSubview:camFocus]; [camFocus setNeedsDisplay]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1.5]; [camFocus setAlpha:0.0]; [UIView commitAnimations]; } } - (void) focus:(CGPoint) aPoint; { Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice"); if (captureDeviceClass != nil) { AVCaptureDevice *device = [captureDeviceClass defaultDeviceWithMediaType:AVMediaTypeVideo]; if([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { CGRect screenRect = [[UIScreen mainScreen] bounds]; double screenWidth = screenRect.size.width; double screenHeight = screenRect.size.height; double focus_x = aPoint.x/screenWidth; double focus_y = aPoint.y/screenHeight; if([device lockForConfiguration:nil]) { [device setFocusPointOfInterest:CGPointMake(focus_x,focus_y)]; [device setFocusMode:AVCaptureFocusModeAutoFocus]; if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]){ [device setExposureMode:AVCaptureExposureModeAutoExpose]; } [device unlockForConfiguration]; } } } } 

添加到Anil的辉煌答案:而不是自己做计算,你应该看看AVCaptureVideoPreviewLayercaptureDevicePointOfInterestForPoint:。 它会给你一个更一致的焦点(从iOS 6和前锋可用)。

 - (void) focus:(CGPoint) aPoint; { Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice"); if (captureDeviceClass != nil) { AVCaptureDevice *device = [captureDeviceClass defaultDeviceWithMediaType:AVMediaTypeVideo]; if([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { CGPoint focusPoint = [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:aPoint]; if([device lockForConfiguration:nil]) { [device setFocusPointOfInterest:CGPointMake(focusPoint.x,focusPoint.y)]; [device setFocusMode:AVCaptureFocusModeAutoFocus]; if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]){ [device setExposureMode:AVCaptureExposureModeAutoExpose]; } [device unlockForConfiguration]; } } } } 

该文档可在这里find: https : //developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureVideoPreviewLayer_Class/index.html#//apple_ref/occ/instm/AVCaptureVideoPreviewLayer/captureDevicePointOfInterestForPoint :

@阿尼尔的回答是一个很好的开始,但它并没有为我工作。 我希望能够让用户继续能够select一个焦点,而不是只有一次(这是他的解决scheme)。 感谢@Anil指引我朝着正确的方向发展。

我的解决scheme有一些差异。

  1. 我希望能够重复使用焦点正方形和animation,而不是只重复一次。
  2. 我希望animation在完成后消失(这是我无法得到@Anil解决scheme的东西。
  3. 而不是使用initWithFrame:我实现了我自己的initWithTouchPoint:
  4. 我有专门的animation焦点行动的方法。
  5. 我也有一个方法来更新帧的位置。
  6. 框架的大小在CameraFocusSquare内,这意味着根据需要更容易查找和更新大小。

CameraFocusSquare.h

 @import UIKit; @interface CameraFocusSquare : UIView - (instancetype)initWithTouchPoint:(CGPoint)touchPoint; - (void)updatePoint:(CGPoint)touchPoint; - (void)animateFocusingAction; @end 

CameraFocusSquare.m

 #import "CameraFocusSquare.h" @implementation CameraFocusSquare { CABasicAnimation *_selectionBlink; } /** This is the init method for the square. It sets the frame for the view and sets border parameters. It also creates the blink animation. */ - (instancetype)initWithTouchPoint:(CGPoint)touchPoint { self = [self init]; if (self) { [self updatePoint:touchPoint]; self.backgroundColor = [UIColor clearColor]; self.layer.borderWidth = 2.0f; self.layer.borderColor = [UIColor orangeColor].CGColor; // create the blink animation _selectionBlink = [CABasicAnimation animationWithKeyPath:@"borderColor"]; _selectionBlink.toValue = (id)[UIColor whiteColor].CGColor; _selectionBlink.repeatCount = 3; // number of blinks _selectionBlink.duration = 0.4; // this is duration per blink _selectionBlink.delegate = self; } return self; } /** Updates the location of the view based on the incoming touchPoint. */ - (void)updatePoint:(CGPoint)touchPoint { CGFloat squareWidth = 50; CGRect frame = CGRectMake(touchPoint.x - squareWidth/2, touchPoint.y - squareWidth/2, squareWidth, squareWidth); self.frame = frame; } /** This unhides the view and initiates the animation by adding it to the layer. */ - (void)animateFocusingAction { // make the view visible self.alpha = 1.0f; self.hidden = NO; // initiate the animation [self.layer addAnimation:_selectionBlink forKey:@"selectionAnimation"]; } /** Hides the view after the animation stops. Since the animation is automatically removed, we don't need to do anything else here. */ - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { // hide the view self.alpha = 0.0f; self.hidden = YES; } @end 

我在一个视图之上启动所有这一切。 这允许我更大的灵活性,并从我的控制器代码分离我的UI代码(想想MVC)。

PreviewView.h

 @import UIKit; @interface PreviewView : UIView - (IBAction)tapToFocus:(UITapGestureRecognizer *)gestureRecognizer; @end 

PreviewView.m

 #import "PreviewView.h" #import "CameraFocusSquare.h" @implementation PreviewView { CameraFocusSquare *_focusSquare; } - (IBAction)tapToFocus:(UITapGestureRecognizer *)gestureRecognizer { CGPoint touchPoint = [gestureRecognizer locationOfTouch:0 inView:self]; if (!_focusSquare) { _focusSquare = [[CameraFocusSquare alloc] initWithTouchPoint:touchPoint]; [self addSubview:_focusSquare]; [_focusSquare setNeedsDisplay]; } else { [_focusSquare updatePoint:touchPoint]; } [_focusSquare animateFocusingAction]; } @end 

最后,在我的UIViewController子类中,我创build了我的UITapGestureRecognizer并将其附加到视图中。 我也在这里实现了我的tap-to-focus代码。

CameraViewController.m

 - (void)viewDidLoad { // do other initialization stuff here // create the tap-to-focus gesture UITapGestureRecognizer *tapToFocusRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToFocus:)]; tapToFocusRecognizer.numberOfTapsRequired = 1; tapToFocusRecognizer.numberOfTouchesRequired = 1; [self.previewView addGestureRecognizer:tapToFocusRecognizer]; } - (IBAction)tapToFocus:(UITapGestureRecognizer *)tapGestureRecognizer { if (!_captureDevice) { return; } if (![_captureDevice isFocusPointOfInterestSupported]) { return; } if (![_captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { return; } [self.previewView tapToFocus:tapGestureRecognizer]; NSError *error; [_captureDevice lockForConfiguration:&error]; if (error) { NSLog(@"Error trying to lock configuration of camera. %@", [error localizedDescription]); return; } CGPoint touchPoint = [tapGestureRecognizer locationOfTouch:0 inView:self.cameraView]; // range of touch point is from (0,0) to (1,1) CGFloat touchX = touchPoint.x / self.previewView.frame.size.width; CGFloat touchY = touchPoint.y / self.previewView.frame.size.height; _captureDevice.focusMode = AVCaptureFocusModeAutoFocus; if ([_captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose]) { _captureDevice.exposureMode = AVCaptureExposureModeAutoExpose; } _captureDevice.focusPointOfInterest = CGPointMake(touchX, touchY); if ([_captureDevice isExposurePointOfInterestSupported]) { _captureDevice.exposurePointOfInterest = CGPointMake(touchX, touchY); } [_captureDevice unlockForConfiguration]; } 

希望这有助于人们,使他们能够移动到更重要的代码!

快速实施:

CameraFocusSquare视图:

 class CameraFocusSquare: UIView,CAAnimationDelegate { internal let kSelectionAnimation:String = "selectionAnimation" fileprivate var _selectionBlink: CABasicAnimation? convenience init(touchPoint: CGPoint) { self.init() self.updatePoint(touchPoint) self.backgroundColor = UIColor.clear self.layer.borderWidth = 2.0 self.layer.borderColor = UIColor.orange.cgColor initBlink() } override init(frame: CGRect) { super.init(frame: frame) } fileprivate func initBlink() { // create the blink animation self._selectionBlink = CABasicAnimation(keyPath: "borderColor") self._selectionBlink!.toValue = (UIColor.white.cgColor as AnyObject) self._selectionBlink!.repeatCount = 3 // number of blinks self._selectionBlink!.duration = 0.4 // this is duration per blink self._selectionBlink!.delegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /** Updates the location of the view based on the incoming touchPoint. */ func updatePoint(_ touchPoint: CGPoint) { let squareWidth: CGFloat = 100 let frame: CGRect = CGRect(x: touchPoint.x - squareWidth / 2, y: touchPoint.y - squareWidth / 2, width: squareWidth, height: squareWidth) self.frame = frame } /** This unhides the view and initiates the animation by adding it to the layer. */ func animateFocusingAction() { if let blink = _selectionBlink { // make the view visible self.alpha = 1.0 self.isHidden = false // initiate the animation self.layer.add(blink, forKey: kSelectionAnimation) } } /** Hides the view after the animation stops. Since the animation is automatically removed, we don't need to do anything else here. */ public func animationDidStop(_ anim: CAAnimation, finished flag: Bool){ if flag{ // hide the view self.alpha = 0.0 self.isHidden = true } } 

}

手势动作:

  open func tapToFocus(_ gesture : UILongPressGestureRecognizer) { if (gesture.state == UIGestureRecognizerState.began) { let touchPoint:CGPoint = gesture.location(in: self.previewView) if let fsquare = self.focusSquare { fsquare.updatePoint(touchPoint) }else{ self.focusSquare = CameraFocusSquare(touchPoint: touchPoint) self.previewView.addSubview(self.focusSquare!) self.focusSquare?.setNeedsDisplay() } self.focusSquare?.animateFocusingAction() let convertedPoint:CGPoint = self.previewLayer!.captureDevicePointOfInterest(for: touchPoint) let currentDevice:AVCaptureDevice = self.videoDeviceInput!.device if currentDevice.isFocusPointOfInterestSupported && currentDevice.isFocusModeSupported(AVCaptureFocusMode.autoFocus){ do { try currentDevice.lockForConfiguration() currentDevice.focusPointOfInterest = convertedPoint currentDevice.focusMode = AVCaptureFocusMode.autoFocus if currentDevice.isExposureModeSupported(AVCaptureExposureMode.continuousAutoExposure){ currentDevice.exposureMode = AVCaptureExposureMode.continuousAutoExposure } currentDevice.isSubjectAreaChangeMonitoringEnabled = true currentDevice.unlockForConfiguration() } catch { } } } }