强制iOS视图不旋转,同时仍允许孩子旋转

我有一个视图控制器与子视图控制器。

tab bar controller | | nav controller | | UIPageViewController (should rotate) | | A (Video Player) (shouldn't rotate) | | B (Controls overlay) (should rotate) 

A应该被迫在任何时候保持肖像,但B应该被允许自由旋转。

我知道shouldAutorotate适用于任何视图控制器及其子,但有什么办法来解决这个问题吗? 好像我可以使用shouldAutorotateToInterfaceOrientation ,但是这在iOS 8中被阻止。

我想保持一个video播放器静态(所以水平video总是水平的,无论设备的方向),而控制层子视图覆盖允许自由旋转。

我正在使用Swift。

我有这个确切的问题,并迅速发现有很多关于自动旋转的坏build议,特别是因为iOS 8处理它不同于以前的版本。

首先,您希望手动应用反转或订阅UIDevice方向更改。 做一个逆旋转仍然会导致一个难看的animation, 设备的方向并不总是相同的界面方向。 理想情况下,您希望相机预览保持真正的冻结状态,并且您的应用用户界面与状态栏的方向和大小相匹配,就像本地相机应用一样。

在iOS 8中改变方向的过程中,窗口本身会旋转,而不是它所包含的视图。 您可以将多个视图控制器的视图添加到单个UIWindow ,但只有rootViewController才会有机会通过shouldAutorotate()进行响应。 即使您在视图控制器级别进行旋转决定,它也是实际旋转的父窗口,从而旋转其所有子视图(包括来自其他视图控制器的子视图)。

解决scheme是两个UIWindow堆叠在一起,每个旋转(或不)具有自己的根视图控制器。 大多数应用程序只有一个,但没有理由你不能有两个和覆盖它们就像任何其他的UIView子类。

这里有一个工作validation的概念, 我也把它放在GitHub上 。 你的具体情况有点复杂,因为你有一堆包含视图控制器,但基本的想法是一样的。 我将在下面介绍一些具体的观点。

 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var cameraWindow: UIWindow! var interfaceWindow: UIWindow! func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { let screenBounds = UIScreen.mainScreen().bounds let inset: CGFloat = fabs(screenBounds.width - screenBounds.height) cameraWindow = UIWindow(frame: screenBounds) cameraWindow.rootViewController = CameraViewController() cameraWindow.backgroundColor = UIColor.blackColor() cameraWindow.hidden = false interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset)) interfaceWindow.rootViewController = InterfaceViewController() interfaceWindow.backgroundColor = UIColor.clearColor() interfaceWindow.opaque = false interfaceWindow.makeKeyAndVisible() return true } } 

interfaceWindow设置一个负插入使其略大于屏幕边界,有效地隐藏了您将看到的黑色矩形遮罩。 通常情况下,您不会注意到,因为蒙版随着窗口旋转,但是由于摄像机窗口被固定,所以在旋转过程中,蒙版在angular落中变得可见。

 class CameraViewController: UIViewController { override func shouldAutorotate() -> Bool { return false } } 

正是你期望的,只需添加你自己的AVCapturePreviewLayer设置。

 class InterfaceViewController: UIViewController { var contentView: UIView! override func viewDidLoad() { super.viewDidLoad() contentView = UIView(frame: CGRectZero) contentView.backgroundColor = UIColor.clearColor() contentView.opaque = false view.backgroundColor = UIColor.clearColor() view.opaque = false view.addSubview(contentView) } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() let screenBounds = UIScreen.mainScreen().bounds let offset: CGFloat = fabs(screenBounds.width - screenBounds.height) view.frame = CGRectOffset(view.bounds, offset, offset) contentView.frame = view.bounds } override func supportedInterfaceOrientations() -> Int { return Int(UIInterfaceOrientationMask.All.rawValue) } override func shouldAutorotate() -> Bool { return true } } 

最后一个技巧是撤销我们应用到窗口的负面插入,我们通过抵消view相同的数量并将contentView视为主视图来实现。

对于你的应用程序, interfaceWindow.rootViewController将是你的标签栏控制器,它又包含一个导航控制器等等。当你的摄像机控制器出现时,所有这些视图都需要是透明的,以便摄像机窗口可以通过它下面显示。 出于性能方面的原因,您可能会考虑将其设置为不透明状态,并且只在相机实际使用时将其设置为透明,并且在相机窗口不hidden时候将相机窗口设置为hidden状态(同时closures捕获会话)。

对不起发表小说 我还没有看到这个地址在其他地方,我花了一段时间才弄清楚,希望它可以帮助你和其他试图获得相同行为的人。 即使是苹果的AVCam示例应用程序也不能很好地处理它。

我发布的示例回购还包括相机已经设置的版本。 祝你好运!

你可以试试这个 –

目标-C代码,如果你有其迅速替代:

 -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { if ()//Place your condition here like if A is visible { return UIInterfaceOrientationMaskPortrait; } return UIInterfaceOrientationMaskAll; } 

您可以订阅旋转更改通知,并在要旋转的子视图上手动设置旋转变换matrix。

我不知道,但我认为你可以为你的子视图创build一个自己的类,并重写shouldAutorotate方法等。这样,它应该覆盖父shouldAutorotate的shouldAutorotate。

简答:不,所有可见的控制器和视图一起旋转(或不旋转)。

很长的回答:

首先,您必须在根控制器中实现自动旋转决策function; 这可能意味着做一个导航控制器的子类。

你可以通过让父视图自动旋转来破解你想要的行为 – 但是让它自己旋转回来看起来是不旋转的。

或者,你不能自动旋转,但听取物理设备旋转的通知,并手动旋转任何你想要的视图,例如: 复制相机应用程序的旋转风景IOS 6 iPhone

另见,fyi:

如何强制iOS 6中的UIViewController纵向

shouldAutoRotate方法不在iOS6中调用

iOS6:supportedInterfaceOrientations不起作用(被调用但界面仍在旋转)

如何实现UIViewController旋转响应方向的变化?

这个问题最简单,最直接的答案就是看Apple的AVCam示例代码。 对我来说关键的部分是:

  1. 使用layerClassAVCaptureVideoPreviewLayer的视图。
  2. 当呈现视图时,设置AVCaptureVideoPreviewLayer连接的videoOrientation以匹配应用程序的statusBarOrientation ,本质上是viewWillAppear(_:)
  3. viewWillTransitionToSize(_:withTransitionCoordinator:)设置videoOrientation以匹配UIDevice.currentDevice().orientation
  4. 启用自动旋转并支持所有界面方向。

我实现了jstn描述的背景窗口方法,效果相当好,但实际情况是它比需要的复杂得多。 AVCam伟大的工作方式相对简单。