如何处理AVCaptureVideoPreviewLayer中的自动转换?

我的应用程序支持除了PortraitUpsideDown以外的所有方向。 在我的视图层次结构中,我有一个AVCaptureVideoPreviewLayer作为UIImageView的顶层视图的子层。 然后在视图下方的层次结构中显示控件的几个叠加视图。

覆盖视图正常工作方向的变化,但我不怎么这个AVCaptureVideoPreviewLayer。 我希望它的行为像在相机应用程序,以便previewLayer保持不变,控制顺利重组。 现在,由于主视图在方向改变时被旋转了,所以我的预览图层也被旋转了,这意味着在横向视图中它保持纵向视图,只取得屏幕的一部分,相机的图片也旋转了90度。 我设法手动旋转预览图层,但是它有这个方向改变animation,这导致在animation期间看到背景。

那么在使previewLayer保持不变的情况下自适应控件的正确方法是什么?

在我的实现中,我已经为我的视图子类化了我想要旋转的UIView,以及像viewController这样的视图,它只是NSObject的一个子类。

在这个视图控制器中,我做了所有与定位变化有关的检查,决定是否应该改变我的目标视图的方向,如果是,那么我调用我的视图的方法来改变它的方向。

首先,我们需要将整个应用程序界面的方向修改为纵向模式,以便我们的ACCaptureVideoPreviewLayer始终保持静止。 这是在MainViewController.h完成的:

 (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation` { return interfaceOrientation==UIInterfaceOrientationPortrait; } 

除了肖像之外,它将返回NO。

为了使我们自定义的viewController能够跟踪设备方向的变化,我们需要使它成为相应通知的观察者:

 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil]; 

我把这些行放在viewController的(void)awakeFromNib方法中。 所以每当设备方向改变时,viewController的方法orientationChanged都会被调用。

其目的是检查设备的新方向是什么,设备的最后方向是什么,并决定是否改变它。 这是实现:

 if(UIInterfaceOrientationPortraitUpsideDown==[UIDevice currentDevice].orientation || lastOrientation==(UIInterfaceOrientation)[UIDevice currentDevice].orientation) return; [[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice].orientation animated:NO]; lastOrientation=[UIApplication sharedApplication].statusBarOrientation; [resultView orientationChanged]; 

如果方向与以前相同或在PortraitUpsideDown中,则不要执行任何操作。 否则,它会将状态栏的方向设置为正确的方向,以便当有来电或骨化时,它将出现在屏幕的正确的一侧。 然后我又在目标视图中调用方法,在这个视图中新的方向所有相应的变化都完成了,比如旋转,resize,移动这个视图中对应于新方向的其他界面元素。

这里是在目标视图中的orientationChanged的实现:

 Float32 angle=0.f; UIInterfaceOrientation orientation=[UIApplication sharedApplication].statusBarOrientation; switch (orientation) { case UIInterfaceOrientationLandscapeLeft: angle=-90.f*M_PI/180.f; break; case UIInterfaceOrientationLandscapeRight: angle=90.f*M_PI/180.f; break; default: angle=0.f; break; } if(angle==0 && CGAffineTransformIsIdentity(self.transform)) return; CGAffineTransform transform=CGAffineTransformMakeRotation(angle); [UIView beginAnimations:@"rotateView" context:nil]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView setAnimationDuration:0.35f]; self.transform=transform; [UIView commitAnimations]; 

当然,在这里您可以添加任何其他更改,如翻译,缩放界面的不同视图,以响应新的方向并为其设置animation。

你也许不需要这个viewController,但是只需要在你的视图的类中进行操作。 希望总体思路清晰。

另外不要忘记,当你不需要他们时,不要忘记获取更改方向的通知:

 [[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; [[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications]; 

iOS 8解决scheme:

 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; } else { self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; } } 

在您的设置代码中:

 self.layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill; if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; } else { self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; } 

主要的问题是,当我得到通知时,状态栏还没有旋转,所以检查当前值实际上给出了旋转之前的值。 所以我在调用检查statusbarorientation和旋转我的子视图的方法之前添加了一些延迟(这里2秒):

 -(void) handleNotification:(NSNotification *) notification { (void) [NSTimer scheduledTimerWithTimeInterval:(2.0) target:self selector:@selector(orientationChanged) userInfo:nil repeats:NO ] ; } 

其余的代码是来自BartoNaz的。