iOS CAlayer方向AVCaptureVideoPreviewLayer不旋转

在这里输入图像说明 总结:我不能强迫CALayer正确地响应方位变化。 每当我尝试使用cgaffinetransform我越来越奇怪的结果(图层不居中)。 任何帮助将不胜感激! 谢谢,

过程我使用AVCaptureVideoPreviewLayer子类添加video预览。 当设备处于纵向时,一切看起来都很好。 设备旋转到横向(左侧或右侧)或纵向颠倒时出现问题。

我使用AVCaptureVideoPreviewLayer子类添加video预览。 当设备处于纵向时,一切看起来都很好。 设备旋转到横向(左侧或右侧)或纵向颠倒时出现问题。

我使用以下代码添加预览图层:

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

并且在纵向模式下正确显示。 当我尝试旋转设备时,预览图层performance奇怪。 它似乎不会调整自身,它不正确旋转。

我试图通过添加下面的方法来解决它

 -(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"); } 

}

添加上面的方法后,我可以看到一个进展。 现在图层正确地reflection当前的设备方向,并且在横向模式下正确显示,但不是在纵向模式下。

图层没有正确定位,它不在屏幕中央(请看屏幕截图)。 要查看发生了什么,我添加了以下debugging语句:

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

并获得以下输出:

 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 

注意debugging输出的第二行。 预览图层的框架被移动128,-128。 任何人都可以解释为什么会发生这种情况,以及如何解决预览图层的方向问题? 谢谢你,Janusz

那这个呢?

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

我仍然不确定是什么原因造成了这个问题,但我设法解决了这个问题。 我是这样做的:

在viewDidLoad我添加一个图层:

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

然后我将调用rotateLayer方法添加到didRotate

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

最后rotateLayer方法如下所示:

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

我仍然不明白为什么它以这种方式工作。 如果有人能解释,这将是伟大的。

这是我在视图控制器中使用的方法来维护捕捉图层的方向,使其始终是正确的:

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

有点罗嗦,但即使在未来任何枚举都有变化,也是安全的。

@Janusz Chudzynski这里是rotateLayer方法的详细解释

这个方法是在检查不同方向如何影响previewLayer时候创build的,所以当方向在LandscapeLeft时候, previewLayer已经检查过了,然后它应该旋转270度,使它处于正确的位置,

 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 

所以创build者已经注意到,如果我将它的LandscapeLeft previewLayer旋转到270度,那么它将处于正确的位置,就像他已经旋转previewLayer每个旋转可能

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

我与@Siegfault,虽然我也发现,当我的视图最初在iPad上的横向加载,方向仍然是正确的。 为了解决这个问题,我在viewDidAppear:调用了相同的委托方法viewDidAppear:使用当前的interfaceOrientation

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

使用willRotateToUserInterfaceOrientation的答案工作正常,除了该方法已被弃用。 所以,如果你能够使用iOS 9,那么在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) } 
 // 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; } 
Interesting Posts