AutoLayout和AVCaptureVideoPreviewLayer

时间:2014-12-18 18:56:49

标签: ios objective-c uiview

我有一个UIView

@property (nonatomic, strong) IBOutlet UIView *previewView;

previewView将最终使用以下预览图层显示我的相机在我的相机上进行的预览。

@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

我在故事板中的previewView上设置了约束,因此遇到了以下问题。

self.previewLayer.frame = self.previewView.bounds; // This doesn't work
self.previewLayer.videoGravity = AVLayerVideoGravityResize;
[self.previewView.layer addSublayer:self.previewLayer];

previewLayer被添加到previewView subLayer中,但它没有显示在正确的参数(宽度和高度)中。我认为这是由于在autolayout上使用约束。

我该如何解决这个问题?

更新:我还尝试了以下内容:

self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
CALayer *rootLayer = [previewView layer];
rootLayer.frame = self.previewView.bounds;
[rootLayer setMasksToBounds:YES];
[self.previewLayer setFrame:[rootLayer bounds]];
[rootLayer addSublayer:self.previewLayer];

它似乎显示如下:http://cl.ly/image/3T3o0O1E3U17

近距离接触,但仍然非常接近。

2 个答案:

答案 0 :(得分:12)

好的,我解决了。

问题在于,当您使用自动布局时,previewLayer的子视图的边框/界限可能会在设置previewLayer后更改,而不会{ {1}}本身也相应更新!这是因为CALayer s的布局完全由一个人自己的代码控制(另见相关答案:https://stackoverflow.com/a/27735617/623685)。

要解决此问题,您必须覆盖viewDidLayoutSubviews的{​​{1}}方法,以便每次子视图的布局更改时更新UIViewController的位置和边界(以及可能的其他位置)

在下文中,我将描述我是如何详细解决的:

我的情况实际上有点复杂,因为我使用的是OpenCV iOS框架2.4.9。这里,预览层由名为previewLayer的OpenCV类创建和管理。因此,我必须创建一个CvVideoCamera的子类,我在其中添加了以下方法:

CvVideoCamera

说明:- (void)updatePreviewLayer { self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); [self layoutPreviewLayer]; } 是您传递给parentView的UIView,以指定预览图层的位置以及应该有多大。 CvVideoCamera是由CvVideoCamera管理的预览图层。 customPreviewLayer是一种基本上更新图层位置的方法:https://github.com/Itseez/opencv/blob/2.4/modules/highgui/src/cap_ios_video_camera.mm#L211

然后我简单地从我的UIViewController调用这个方法,如下所示:

layoutPreviewLayer

答案 1 :(得分:1)

主要思想是相同的 - 当使用Autolayout时,需要在应用所有视图约束后设置预览图层的框架。 viewDidLayoutSubviews方法用于此 - 与原始答案相同,但没有使用除Apple AVFoundation以外的任何框架的复杂性。

以下代码基于Apple的示例(AVCam-iOS: Using AVFoundation to Capture Images and Movies)。但是代码被修改为使用updateViewConstraints方法以编程方式设置所有约束,而不是样本中使用的Storyboard方法。相关片段如下所示:

@interface QRScannerViewController ()

@property (nonatomic) UIView *previewView;

@end

@implementation QRScannerViewController
{
    AVCaptureSession *_captureSession;
    AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer;
    dispatch_queue_t _sessionQueue;
}

#pragma mark - Lifecycle

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addSubview:self.previewView];

    _captureSession = [AVCaptureSession new];
    _captureVideoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
    _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    _sessionQueue = dispatch_queue_create("av_session_queue", DISPATCH_QUEUE_SERIAL);

    [self.view setNeedsUpdateConstraints];

    dispatch_async(_sessionQueue, ^{
        [self configureCaptureSession];
    });

    [self.previewView.layer addSublayer:_captureVideoPreviewLayer];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (_isCaptureDeviceConfigured) {
        [_captureSession startRunning];
    }
}

- (void)viewDidLayoutSubviews
{
    _captureVideoPreviewLayer.frame = self.previewView.bounds;
}

#pragma mark - getters / initializers

- (UIView *)previewView
{
    if (_previewView) {
        return _previewView;
    }
    _previewView = [UIView new];
    _previewView.backgroundColor = [UIColor blackColor];
    _previewView.translatesAutoresizingMaskIntoConstraints = NO;

    return _previewView;
}

- (void)configureCaptureSession
{
    [_captureSession beginConfiguration];

    // Code omitted, only relevant to the preview layer is included. See the Apple's sample for full code.
    ...
    _captureVideoPreviewLayer.connection.videoOrientation = initialVideoOrientation;
    ...
}

#pragma mark - Autolayout

- (void)updateViewConstraints
{
    [self.previewView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant:0.0].active = YES;
    [self.previewView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor constant:-40.0].active = YES;
    [self.previewView.widthAnchor constraintEqualToConstant:300].active = YES;
    [self.previewView.heightAnchor constraintEqualToAnchor:self.previewView.widthAnchor constant:0.0].active = YES;

    [super updateViewConstraints];
}