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