只锁定一个 ViewController 的方向为横向,其余为纵向

Lock only one ViewController's orientation to landscape, and the remaining to portrait

我想将所有 ViewController 的方向锁定为纵向,但其中一个除外,当它被推送时始终处于 landscapeRight。 我尝试了很多解决方案,使用 UINavigationController 的扩展名,覆盖 supportedInterfaceOrientationsshouldAutorotate 但没有运气。

我找到了解决方案,目前有效。

  1. 在每个视图控制器上,您应该放置此代码以仅支持所需的旋转(示例中的 landscapeRight):

    override var shouldAutorotate: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.landscapeRight
    }
    
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .landscapeRight
    }
    

    另一方面实现相同的方法,但纵向。

  2. UINavigationController

    创建扩展
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return (self.topViewController?.supportedInterfaceOrientations)!
    }
    
    open override var shouldAutorotate: Bool {
        return true
    }
    

注意:同样在项目的目标中启用所有需要的支持 方向。

我想在横屏模式下展示的唯一一个控制器,我以模态方式呈现。

如果您使用导航控制器来呈现视图,您会 运行 感到困惑。例如,我们通常通过调用 performSegueWithIdentifier:sender:pushViewController:animated:.

等导航控制器实例函数来利用导航控制器来处理添加视图。

推送不会像您希望的那样工作。

并且 segues 可能也不起作用,特别是如果您在 IB 中创建了导航控制器之外的 segues!

无论如何,如果您以某种方式成功地覆盖了 "parent" 导航控制器的方向,您可能会 运行 冒着进入边缘情况的风险,并且您可能正在使用一些粗略的东西,可能会失去任何支持更新。

也就是说,我找到的解决方法是:

步骤 1

使用presentViewController:animated:completion:呈现横向右视图控制器:

// assuming you're using IB and Main bundle
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let landscapeViewController = storyboard.instantiateViewController(withIdentifier: "LandscapeViewControllerStoryboardID")
self.present(landscapeViewController, animated: true, completion: {
    print("landscapeViewController presented")
})

步骤 2

将以下覆盖添加到您的 landscapeViewController 现在实际上可以工作了:

/* override the preferredInterfaceOrientationForPresentation for a view controller
that is intended to be presented full screen in a specific orientation.
https://developer.apple.com/reference/uikit/uiviewcontroller */
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return .landscapeRight
}

步骤 3(可选)

所有这些工作完成后,当您关闭 landscapeViewController 时仍然有些奇怪:它现在也是横向的。

这是因为导航控制器玩的不错。换句话说,现在轮到导航控制器尝试强制改变方向,强制它回到原来的样子。

为此,您需要在 present landscapeViewController 之前 存储设备的方向。在显示 landscapeViewController 的视图控制器中,我将其存储在一个实例变量中:

let ratio = self.view.bounds.width / self.view.bounds.height
if (ratio > 1) { // horizontal/landscape
    self.currentOrientationIsPortrait = false // read by navigation controller when dismissing full screen view player
} else {
    self.currentOrientationIsPortrait = true // read by navigation controller when dismissing full screen view player
}

然后,我在我的 navigationController class' 首选方向覆盖中测试该视图控制器实例的值。假设我从中呈现 landscapeViewController 的视图控制器称为 presentingViewController:

// execution order: during visibileViewController's dismissal
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    get {

        if visibleViewController != nil {

            // check if landscapeViewController is being dismissed
            if (visibleViewController as? LandscapeViewController) != nil {

                // check if the top view controller is one that tracks its orientation (currently only presentingViewController)
                if let presentingViewController = self.viewControllers.last as? PresentingViewController {

                    if presentingViewController.currentOrientationIsPortrait {
                        return .portrait // TODO should we not support portraitUpsideDown?
                    }
                }
            }
            // otherwise, return whatever
            return visibleViewController!.preferredInterfaceOrientationForPresentation
        }
        // otherwise, return whatever
        return super.preferredInterfaceOrientationForPresentation
    }
}

而且,宾果游戏。 (希望如此)

编辑

另外不要忘记将此代码放在其他 ViewControllers 中:

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return .portrait
}