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

时间:2015-02-02 12:44:40

标签: ios swift uiviewcontroller autorotate

我有一个带有子视图控制器的视图控制器。

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中被屏蔽了。

我想让视频播放器保持静态(因此水平视频始终是水平的,无论设备方向如何),而控制层子视图叠加层可以自由旋转。

我正在使用Swift。

6 个答案:

答案 0 :(得分:18)

我遇到了这个确切的问题,很快发现有很多关于自动旋转的错误建议,特别是因为iOS 8处理它与以前的版本不同。

首先,您想要手动应用反向旋转或订阅UIDevice方向更改。执行反向旋转仍会导致难看的动画,设备方向并不总是与界面方向相同。理想情况下,您希望相机预览保持真正冻结状态,并使您的应用UI与状态栏方向和尺寸相匹配,就像本机相机应用程序一样。

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

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

这是一个工作概念验证,which I've also put on GitHub here。你的特殊情况有点复杂,因为你有一堆包含视图控制器,但基本思路是一样的。我将在下面谈谈一些具体问题。

@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上设置负插入使其略大于屏幕边界,有效地隐藏了您看不到的黑色矩形蒙版。通常您不会注意到因为蒙版随窗口一起旋转,但由于相机窗口是固定的,因此在旋转过程中,蒙版在角落中变得可见。

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(如果不是(同时也关闭捕获会话))。

很抱歉发表一本小说;我还没有在其他任何地方看到这个问题,我花了一段时间才弄明白,希望它可以帮助你和其他任何试图获得相同行为的人。即便是Apple的AVCam示例应用程序也不能正确处理它。

The example repo I posted还包含已设置相机的版本。祝你好运!

答案 1 :(得分:1)

你可以试试这个 -

目标-C代码如果您在swift中有其替代方法:

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

我不确定,但我认为您可以为子视图创建一个自己的类并覆盖shouldAutorotate方法等。这样它应该覆盖来自parent-viewcontroller的shouldAutorotate

答案 4 :(得分:0)

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

答案很长:

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

您可以通过让父视图自动旋转来破解您想要的行为 - 但让它自己手动旋转返回以显示未旋转。

或者,您不能自动旋转,但会听取物理设备旋转的通知,并手动旋转您想要的任何视图,例如:Replicate camera app rotation to landscape IOS 6 iPhone

另见,fyi:

How to force a UIViewController to Portrait orientation in iOS 6

shouldAutoRotate Method Not Called in iOS6

iOS6: supportedInterfaceOrientations not working (is invoked but the interface still rotates)

How to implement UIViewController rotation in response to orientation changes?

答案 5 :(得分:0)

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

  1. 使用layerClassAVCaptureVideoPreviewLayer
  2. 的视图
  3. 设置视图时,设置AVCaptureVideoPreviewLayer连接的videoOrientation以匹配应用程序的statusBarOrientation,基本上为viewWillAppear(_:)
  4. videoOrientation设置为与UIDevice.currentDevice().orientation中的viewWillTransitionToSize(_:withTransitionCoordinator:)匹配。
  5. 启用自动旋转并支持所有界面方向。
  6. 我实现了jstn描述的背景窗口方法并且它工作得相当好,但实际情况是它比必要的复杂得多。 AVCam效果很好,方法相对简单。