22

我正在开发一个 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 框架显示这个反馈矩形或者我必须自己实现这个功能?

4

5 回答 5

36

这是我所做的:这是创建当用户点击相机覆盖时显示的正方形的类。

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];
            }
        }
    }
}
于 2013-04-18T10:02:35.997 回答
13

添加到 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 :

于 2014-09-28T15:13:06.760 回答
9

快速实现:

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 {

            }
        }
    }
}
于 2016-03-31T07:45:14.760 回答
8

@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)。

预览视图.h

@import UIKit;

@interface PreviewView : UIView

- (IBAction)tapToFocus:(UITapGestureRecognizer *)gestureRecognizer;

@end

预览视图.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创建并附加到视图。我还在这里实现了我的点击聚焦代码。

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

希望这对人们有所帮助,以便他们可以转向更重要的代码!

于 2015-06-16T23:33:20.693 回答
2

这是一个基本的 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") }

}
于 2020-01-10T04:52:15.750 回答