2013-03-16 68 views
15

我开发一个iPhone应用程序,我直接使用AVFoundation通过摄像机捕捉到的视频。AVFoundation触摸对焦反馈矩形

我已经实现了一个功能,使为用户tap to focus功能。

- (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 
} 

到目前为止很好,但我缺少像照片应用程序中的反馈矩形。有什么办法可以告诉AVFoundation Framework显示这个反馈矩形,还是我必须自己实现这个功能?

+1

无关你的问题,但是,你应该使用'如果( CaptureDeviceClass!=无)' – 2013-03-16 14:20:52

+0

谢谢你的提示。我现在还没有真正关心和了解这些差异,所以我查了一下。由于我引用一个类的事实,你是绝对正确的。 – Alexander 2013-03-16 15:22:57

+0

@亚历山大,我面临与你一样的问题。你解决了这个问题吗? – ttotto 2013-04-05 09:27:12

回答

34

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

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]; 
      } 
     } 
    } 
} 
+0

代码中的“camFocus”是什么? – iqueqiorio 2015-04-16 03:58:25

+0

@iqueqiorio camFocus是CameraFocusSquare类型的变量,自定义类(代码的第一个片段)。 – Anil 2015-04-16 07:45:17

+0

好吧,我该如何声明,我正在尝试'CameraFocusSquare * camFocus = [[CameraFocusSquare alloc] init];'但是这给我一个错误? – iqueqiorio 2015-04-16 18:21:57

11

添加到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]; 
      } 
     } 
    } 
} 

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

6

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

有我的解决方案存在一些差异。

  1. 我希望能够重复使用焦点正方形和动画,而不是只重复一次。
  2. 我想要的动画消失后,它完成(的东西,我不能让@ Anil的解决方案做。
  3. 而不是使用initWithFrame:的,我实现我自己的initWithTouchPoint:
  4. 我有专门动画的方法聚焦动作。
  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子类中,我有我的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]; 
} 

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

6

迅速实施:

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 { 

      } 
     } 
    } 
}