AutoLayout and AVCaptureVideoPreviewLayer

I have a UIView

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

previewView will ultimately display a preview of what is happening in my camera on my iPhone using the next preview layer.

 @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 

I have limitations set on my previewView in storyboards, and so I ran into the following problem.

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

previewLayer added to the preview subLayer, but it does not appear in the correct parameters (width and height). I think this is due to the use of startup restrictions.

How can i fix this?

Update: I also tried the following:

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

And it looks like this: http://cl.ly/image/3T3o0O1E3U17

Close, but still very strong.

+6
source share
2 answers

Ok, I solved it.

The problem is that when using autorun the frames / borders of your subframes, the UIViewController may change after you set up your previewLayer , without updating the previewLayer ! This is because the CALayer layout CALayer controlled by only one native code (see also this related answer: fooobar.com/questions/979878 / ... ).

To solve this problem, you must override the viewDidLayoutSubviews method of your UIViewController to update the position and previewLayer frames (and possibly others) every time you change the layout of the subview.


In the future, I will describe how I solved this in detail:

My case was actually a bit more complicated because I am using OpenCV iOS framework 2.4.9. Here, a preview layer is created and managed by an OpenCV class called CvVideoCamera . So I had to create a subclass of CvVideoCamera , to which I added the following method:

 - (void)updatePreviewLayer { self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); [self layoutPreviewLayer]; } 

Explanation: parentView is a UIView that you pass to CvVideoCamera to indicate where the preview layer should be located and how much it should be. customPreviewLayer is a preview layer managed by CvVideoCamera. And layoutPreviewLayer is a method that basically updates the position of the layer: https://github.com/Itseez/opencv/blob/2.4/modules/highgui/src/cap_ios_video_camera.mm#L211

Then I just called this method from my UIViewController as follows:

 - (void)viewDidLayoutSubviews { NSLog(@"viewDidLayoutSubviews"); [super viewDidLayoutSubviews]; [self.myCameraView updatePreviewLayer]; } 
+10
source

The basic idea is the same - when using Autolayout frame of the preview layer must be set after applying all the limitations of the view. To do this, use the viewDidLayoutSubviews method - the same as in the original answer, but without the complications of using any framework other than Apple AVFoundation .

The following code is based on a sample of Apple ( AVCam-iOS: using AVFoundation to capture images and movies ). But the code has been modified to use the updateViewConstraints method to programmatically set all restrictions instead of the Storyboard approach used in the sample. The relevant fragments are shown below:

 @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 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]; } 
+1
source

Source: https://habr.com/ru/post/979877/


All Articles