AVFoundation点击以聚焦反馈矩形

时间:2013-03-16 12:31:08

标签: iphone ios ipad avfoundation

我正在开发一个iphone应用程序,我直接使用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显示这个反馈矩形还是我必须自己实现这个功能?

5 个答案:

答案 0 :(得分: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];
            }
        }
    }
}

答案 1 :(得分:12)

添加Anil的精彩答案:您应该查看 AVCaptureVideoPreviewLayer captureDevicePointOfInterestForPoint:,而不是自己进行计算。它将为您提供更加一致的焦点(可从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

答案 2 :(得分:7)

快速实施:

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 {

            }
        }
    }
}

答案 3 :(得分:6)

@Anil的答案是一个很好的开始,但它对我没有用。我希望能够让用户继续能够选择一个焦点,而不是只选择一次(这是他的解决方案所做的)。感谢@Anil指出我正确的方向。

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

  1. 我希望能够重复使用焦点方块和动画,而不是只重复一次。
  2. 我希望动画在完成后消失(我无法获得@ Anil&#39的解决方案。
  3. 我没有使用initWithFrame:,而是实施了自己的initWithTouchPoint:
  4. 我有一种专门设置焦点动作动画的方法。
  5. 我还有一种更新框架位置的方法。
  6. 框架的尺寸在CameraFocusSquare之内,这意味着可以根据需要更轻松地查找和更新尺寸。
  7. <强> 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];
    }
    

    希望这可以帮助人们,以便他们可以转移到更重要的代码!

答案 4 :(得分:1)

这是一个基本的Swift视图,将显示一个动画的焦点正方形。只需将其添加到相机视图中,然后将其连接到轻击手势识别器的焦点回调即可。

@objc func didTapToFocus(gesture: UITapGestureRecognizer) {
    let pointInViewCoordinates = gesture.location(in: gesture.view)
    let pointInCameraCoordinates = cameraView.videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: pointInViewCoordinates)
    camera.focusOn(pointInCameraCoordinates: pointInCameraCoordinates)
    cameraView.showFocusBox(at: pointInViewCoordinates)
}

焦点视图:

final class CameraFocusBoxView: UIView {

    // MARK: - Instantiation

    init() {
        super.init(frame: .zero)

        backgroundColor = .clear
        layer.addSublayer(focusBoxLayer)
    }

    // MARK: - API

    /// This zooms/fades in a focus square and blinks it a few times, then slowly fades it out
    func showBox(at point: CGPoint) {
        focusBoxLayer.removeAllAnimations()
        let scaleKey = "zoom in focus box"
        let fadeInKey = "fade in focus box"
        let pulseKey = "pulse focus box"
        let fadeOutKey = "fade out focus box"
        guard focusBoxLayer.animation(forKey: scaleKey) == nil,
              focusBoxLayer.animation(forKey: fadeInKey) == nil,
              focusBoxLayer.animation(forKey: pulseKey) == nil,
              focusBoxLayer.animation(forKey: fadeOutKey) == nil
            else { return }

        CATransaction.begin()
        CATransaction.setDisableActions(true)
        focusBoxLayer.position = point
        CATransaction.commit()

        let scale = CABasicAnimation(keyPath: "transform.scale")
        scale.fromValue = 1
        scale.toValue = 0.375
        scale.duration = 0.3
        scale.isRemovedOnCompletion = false
        scale.fillMode = .forwards

        let opacityFadeIn = CABasicAnimation(keyPath: "opacity")
        opacityFadeIn.fromValue = 0
        opacityFadeIn.toValue = 1
        opacityFadeIn.duration = 0.3
        opacityFadeIn.isRemovedOnCompletion = false
        opacityFadeIn.fillMode = .forwards

        let pulsing = CABasicAnimation(keyPath: "borderColor")
        pulsing.toValue = UIColor(white: 1, alpha: 0.5).cgColor
        pulsing.repeatCount = 2
        pulsing.duration = 0.2
        pulsing.beginTime = CACurrentMediaTime() + 0.3 // wait for the fade in to occur

        let opacityFadeOut = CABasicAnimation(keyPath: "opacity")
        opacityFadeOut.fromValue = 1
        opacityFadeOut.toValue = 0
        opacityFadeOut.duration = 0.5
        opacityFadeOut.beginTime = CACurrentMediaTime() + 2 // seconds
        opacityFadeOut.isRemovedOnCompletion = false
        opacityFadeOut.fillMode = .forwards

        focusBoxLayer.add(scale, forKey: scaleKey)
        focusBoxLayer.add(opacityFadeIn, forKey: fadeInKey)
        focusBoxLayer.add(pulsing, forKey: pulseKey)
        focusBoxLayer.add(opacityFadeOut, forKey: fadeOutKey)
    }

    // MARK: - Private Properties

    private lazy var focusBoxLayer: CALayer = {
        let box = CALayer()
        box.bounds = CGRect(x: 0, y: 0, width: 200, height: 200)
        box.borderWidth = 2
        box.borderColor = UIColor.white.cgColor
        box.opacity = 0
        return box
    }()

    // MARK: - Unsupported Initializers

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }

}