IOS CAlayer Orientation AVCaptureVideoPreviewLayer not spinning

enter image description here Summary: I cannot get CALayer to respond correctly to orientation changes. Whenever I try to use cgaffinetransform, I get strange results (the layer is not centered). Any help would be appreciated! thanks,

Process I am adding a video preview using a subclass of AVCaptureVideoPreviewLayer. When the device is in portrait orientation, everything looks great. The problem occurs when the device rotates in landscape orientation (left or right) or portrait upside down.

I am adding a video preview using a subclass of AVCaptureVideoPreviewLayer. When the device is in portrait orientation, everything looks great. The problem occurs when the device rotates in landscape orientation (left or right) or portrait upside down.

I am adding a preview layer using the following code:

CGRect layerRect = [[[self view] layer] bounds]; [[[self captureManager] previewLayer] setBounds:layerRect]; [[[self captureManager] previewLayer]setFrame:CGRectMake(0, height, width, height)]; [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; 

And it displays correctly in portrait mode. When I try to rotate the device, the preview layer behaves strangely. It does not seem to resize, and it does not rotate correctly.

I tried to fix this by adding the following method

 -(void)rotateLayer{ CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; UIDeviceOrientation orientation =[[UIDevice currentDevice]orientation]; switch (orientation) { case UIDeviceOrientationLandscapeLeft: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); // 270 degress NSLog(@"Landscape Left"); [stuckview setPosition: CGPointMake(self.view.bounds.size.width /2.0, self.view.bounds.size.height /2.0)]; break; case UIDeviceOrientationLandscapeRight: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI_2); // 90 degrees NSLog(@"Landscape Right"); [stuckview setPosition: CGPointMake(self.view.bounds.size.width /2.0, self.view.bounds.size.height /2.0)]; break; case UIDeviceOrientationPortraitUpsideDown: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI); // 180 degrees NSLog(@"Landscape Upside down"); break; default: stuckview.affineTransform = CGAffineTransformMakeRotation(0.0); break; } float h1 = stuckview.frame.size.height; float w1 = stuckview.frame.size.width; if(UIDeviceOrientationIsPortrait(orientation)) { stuckview.position =CGPointMake(h1/2.0, w1/2.0); NSLog(@"Portrait"); } else{ stuckview.position =CGPointMake(w1/2.0, h1/2.0); NSLog(@"Portrait"); } 

}

After adding the above method, I see progress. Now the layer rotates correctly, reflecting the current orientation of the device, and it displays correctly in landscape mode, but NOT in portrait mode.

The layer is not set correctly, it is not centered on the screen (look at the screenshot). To find out what happened, I added the following debug statements:

 CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; float h = stuckview.bounds.size.height; float w = stuckview.bounds.size.width; float x = stuckview.bounds.origin.x; float y = stuckview.bounds.origin.y; float h1 = stuckview.frame.size.height; float w1 = stuckview.frame.size.width; float x1 = stuckview.frame.origin.x; float y1 = stuckview.frame.origin.y; NSLog(@"%f %f %f %f ", h,w,x,y ); NSLog(@"%f %f %f %f ", h1,w1,x1,y1 ); NSLog(@"Anchor Point: %f %f",stuckview.anchorPoint.x, stuckview.anchorPoint.y); NSLog(@"Position: %f %f",stuckview.position.x, stuckview.position.y); CGAffineTransform at = stuckview.affineTransform; NSLog(@"Affine Transform After : %f %f %f %f %f %f %f", at.a,at.b, at.c, at.d, at.tx,at.tx, at.ty); 

And get the following result:

 2012-09-30 13:25:12.067 RotatePreviewLayer[2776:907] 1024.000000 768.000000 0.000000 0.000000 

2012-09-30 RotatePreviewLayer [2776: 907] 1024.000000 768.000000 128.000000 -128.000000

 2012-09-30 13:25:12.070 RotatePreviewLayer[2776:907] Portrait 2012-09-30 13:25:12.072 RotatePreviewLayer[2776:907] Anchor Point: 0.500000 0.500000 2012-09-30 13:25:12.074 RotatePreviewLayer[2776:907] Position: 512.000000 384.000000 2012-09-30 13:25:12.076 RotatePreviewLayer[2776:907] Affine Transform after: -1.000000 0.000000 -0.000000 -1.000000 0.000000 0.000000 0.000000 

Pay attention to the second line of debug output. The preview layer frame moves to 128, -128. Can someone explain to me why this is happening and how to fix orientation problems using the preview layer? Thank you Janusz

+6
source share
8 answers

I'm still not sure what causes the problem, but I managed to fix it. Here is how I did it:

in viewDidLoad I am adding a layer:

 CGRect layerRect = [[[self view] layer] bounds]; [[[self captureManager] previewLayer] setBounds:layerRect]; [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; [[[self view] layer] addSublayer:[[self captureManager] previewLayer]]; 

Then I add a call to the rotateLayer method for didRotate

 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ [self rotateLayer]; } 

and finally, the rotateLayer method looks like this:

 -(void)rotateLayer{ CALayer * stuckview = [[self captureManager] previewLayer]; CGRect layerRect = [[[self view] layer] bounds]; UIDeviceOrientation orientation =[[UIDevice currentDevice]orientation]; switch (orientation) { case UIDeviceOrientationLandscapeLeft: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); // 270 degress break; case UIDeviceOrientationLandscapeRight: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI_2); // 90 degrees break; case UIDeviceOrientationPortraitUpsideDown: stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI); // 180 degrees break; default: stuckview.affineTransform = CGAffineTransformMakeRotation(0.0); [stuckview setBounds:layerRect]; break; } [stuckview setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; } 

I still don't understand why it works this way. If anyone can explain, it will be great.

+5
source

How about this?

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { _videoPreviewLayer.connection.videoOrientation = toInterfaceOrientation; } 
+8
source

This is the method that I use in the view controller to maintain the orientation of the capture layer, so that it always has the right side:

 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration if (_previewLayer.connection.supportsVideoOrientation) { switch (toInterfaceOrientation) { case UIInterfaceOrientationPortrait: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; break; } case UIInterfaceOrientationPortraitUpsideDown: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; break; } case UIInterfaceOrientationLandscapeLeft: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; break; } case UIInterfaceOrientationLandscapeRight: { _previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; break; } } } 

A bit verbose, but safe, even if any listing ever changes in the future.

+5
source

@Janusz Chudzynski here is a detailed explanation of what the rotateLayer method does

This method is created after studying how a different orientation affects the previewLayer , so the creator was checked when the orientation is in LandscapeLeft , then it must rotate 270 degrees to make it in the correct position for what it used

 stuckview.affineTransform = CGAffineTransformMakeRotation(M_PI+ M_PI_2); M_PI 3.14159265358979323846264338327950288 // pi = 180 degree + M_PI_2 1.57079632679489661923132169163975144 // pi/2 = 90 degree // Total = 270 degree 

so the creator noticed that if I previewLayer to 270 degrees when it is in LandscapeLeft , then it will be in the correct position, just as it turned the previewLayer for every possible rotation

+1
source
 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { previewLayer.orientation = toInterfaceOrientation; } 
0
source

I'm with @Siegfault, although I also found that when my view loads in landscape orientation on the iPad initially, the orientation is still correct. To fix the error, I call the same delegate method in viewDidAppear: with the current interfaceOrientation :

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self willRotateToInterfaceOrientation:self.interfaceOrientation duration:0.0]; } 
0
source

An answer using willRotateToUserInterfaceOrientation works fine except that this method is deprecated. So, if you can use iOS 9, then here's the way to do it, in Swift:

 override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { let newOrientation = UIDevice.currentDevice().orientation switch newOrientation { case .LandscapeLeft: self.capturePreviewLayer?.connection.videoOrientation = .LandscapeLeft case .LandscapeRight: self.capturePreviewLayer?.connection.videoOrientation = .LandscapeRight case .Portrait, .Unknown, .FaceUp, .FaceDown: self.capturePreviewLayer?.connection.videoOrientation = .Portrait case .PortraitUpsideDown: self.capturePreviewLayer?.connection.videoOrientation = .PortraitUpsideDown } super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator) } 
0
source
 // layout iOS 8+ animated - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { NSString *timingFunc = nil; switch ( [context completionCurve] ) { case UIViewAnimationCurveEaseIn: timingFunc = kCAMediaTimingFunctionEaseIn; break; case UIViewAnimationCurveEaseInOut: timingFunc = kCAMediaTimingFunctionEaseInEaseOut; break; case UIViewAnimationCurveEaseOut: timingFunc = kCAMediaTimingFunctionEaseOut; break; case UIViewAnimationCurveLinear: timingFunc = kCAMediaTimingFunctionLinear; break; } [CATransaction begin]; [CATransaction setAnimationDuration:[context transitionDuration]]; [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:timingFunc]]; [self updatePreviewLayer]; [CATransaction commit]; UIInterfaceOrientation toOrientation = [[UIApplication sharedApplication] statusBarOrientation]; // layout ui if needed } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { }]; [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; } - (void)updatePreviewLayer { CGAffineTransform transform = CGAffineTransformIdentity; switch ( UIDevice.currentDevice.orientation ) { case UIDeviceOrientationLandscapeLeft: transform = CGAffineTransformRotate(transform, -M_PI_2); break; case UIDeviceOrientationLandscapeRight: transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIDeviceOrientationPortraitUpsideDown: transform = CGAffineTransformRotate(transform, M_PI); break; default: break; } preview.affineTransform = transform; preview.frame = self.view.bounds; } 
0
source

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


All Articles