【问题标题】:How to maintain presenting view controller's orientation when dismissing modal view controller?关闭模式视图控制器时如何保持呈现视图控制器的方向?
【发布时间】:2014-08-05 10:49:21
【问题描述】:

我正在开发这个应用程序,我需要我的所有视图控制器,但只有一个是纵向的。 一个特别的单视图控制器,我需要它能够旋转到手机所处的任何方向。

为此,我以模态方式呈现它(未嵌入到 NavigationController 中)

所以(例如)我的结构是这样的:

  • 窗口 - 纵向
    • 根视图控制器 (UINavigationController - Portrait)
      • 主视图控制器 (UIViewController - Portrait)
        • 详细视图控制器 (UIViewController - Portrait)
        • .
        • .
        • 模态视图控制器(UIVIewController - 全部)

现在,当我在横向位置关闭模态视图控制器时,我的父视图控制器也会旋转,即使它不支持该方向。

应用程序中的所有UIViewControllersUINavigaionControllers 都继承自实现了这些方法的相同通用类:

override func supportedInterfaceOrientations() -> Int
{
    return Int(UIInterfaceOrientationMask.Portrait.toRaw())
}

我的模态视图控制器再次覆盖了这个方法,它看起来像这样:

override func supportedInterfaceOrientations() -> Int
{
    return Int(UIInterfaceOrientationMask.All.toRaw())
}

更新 1

这似乎只发生在 iOS8 Beta 上。 有人知道视图控制器的旋转是否发生了变化,或者这只是测试版中的一个错误?

【问题讨论】:

标签: ios iphone uiviewcontroller swift screen-rotation


【解决方案1】:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ([self.window.rootViewController.presentedViewController isKindOfClass: [SecondViewController class]])
{
    SecondViewController *secondController = (SecondViewController *) self.window.rootViewController.presentedViewController;

    if (secondController.isPresented)
        return UIInterfaceOrientationMaskAll;
    else return UIInterfaceOrientationMaskPortrait;
}
else return UIInterfaceOrientationMaskPortrait;
}

对于斯威夫特

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow) -> Int {

    if self.window?.rootViewController?.presentedViewController? is SecondViewController {

        let secondController = self.window!.rootViewController.presentedViewController as SecondViewController

        if secondController.isPresented {
            return Int(UIInterfaceOrientationMask.All.toRaw());
        } else {
            return Int(UIInterfaceOrientationMask.Portrait.toRaw());
        }
    } else {
        return Int(UIInterfaceOrientationMask.Portrait.toRaw());
    }

}

更多详情请查看link

【讨论】:

  • 您已经有一段时间没有回答了,但今天我又遇到了这个问题。您的解决方案有效!谢谢!
  • 这对我不起作用,UIViewController 上没有 isPresented 属性
  • @RicSantos 有关完整详细信息,请参阅上述答案中的附加链接。视图控制器中添加了一个属性。
  • 这不适用于 UI 页面视图控制器。我的页面视图控制器中有 2 个视图控制器(A,B)。这段代码所有的断点都正确命中,但是当我从 B(横向)移动到 A(纵向)时,我仍然以横向结束。
  • 链接目前已失效 (500)。请在您的回答中包含相关细节。幸运的是,存档现在有它:web.archive.org/web/20170628051957/http://swiftiostutorials.com/…
【解决方案2】:

我在一个应用程序上遇到了同样的问题,经过几天的实验,我想出了一个不是很好但现在可以工作的解决方案。我在 appdelegate 中使用了委托方法application:supportedInterfaceOrientationsForWindow:

我创建了一个测试项目并将其放在here on github(包括显示结果的 GIF...)

// 注意:它不是很快,但我希望它无论如何都会有所帮助

【讨论】:

  • 我遇到了类似的问题,并像你一样解决了同样的问题,但在这里解释了另一个问题,stackoverflow.com/questions/25769068/…
  • 在这个主题上花费了大部分时间之后,我在您的答案中找到了解决方案。这对我来说很有意义,并且在我的控制器中完美无缺。非常感谢。
【解决方案3】:

经过大量实验,我确信这是 iOS 8 的“功能”。

如果你仔细想想,这完全有道理,因为它已经出现了很长时间。

  • 在 iOS 4 中,可以在标签栏控制器和导航控制器中更改视图控制器时强制应用旋转,以及在呈现/关闭控制器时强制应用旋转。

  • 然后在 iOS 6 中,强制应用旋转变得不可能,除非在呈现/关闭视图控制器时(正如我在许多答案中解释的那样,例如 this one)。

  • 现在,在 iOS 8 中,我推测根本不可能强制应用轮换(启动时除外)。它可以首选某个方向,因此一旦处于该方向,它将停留在那里,但它不能强制应用程序进入该方向。

    相反,您的视图控制器应该“适应”。有几个 WWDC 2014 视频专注于“适应”,现在我开始明白这是它如此重要的原因之一。

    编辑:在种子 4 中,这个功能(强制轮换展示和解雇)似乎正在回归!

【讨论】:

  • 举个例子,我发现商店工具包警报现在可以在横向中找到。他们不能在横向模式下工作的事实是您需要在使用 Store Kit 之前强制应用程序进入纵向模式的原因。现在这个理由没有了。这表明 Apple 一直在检查框架并消除强制轮换的理由——这反过来又表明它不能再这样做了。
  • 嗯...不过这看起来很奇怪。我的意思是这意味着我的应用程序必须能够以横向和纵向显示所有屏幕?因为在我上面的示例中,我的应用程序仅是纵向的,但有一个以模态方式呈现的视图,即照片库。在我看来,在父 VC 上使用首选方向似乎无法正常工作,因为呈现的子 VC 在隐藏时会以模态方式旋转我的父级。
  • 您究竟是如何“强制旋转”的?在将支持横向和纵向的模态视图控制器解除到仅支持纵向的呈现视图控制器后,我仍然遇到问题
  • @johosher 我仍然看到一些边缘情况,因此请务必将任何可重现的情况打包为错误报告并报告!如果 iOS 8 在没有他们处理的情况下完成,那将会很糟糕。
  • @matt 你有办法在 iOS 8 中强制旋转视图控制器吗?
【解决方案4】:

我们部署了一个具有横向控制器的应用程序,该控制器提供一个仅纵向视图控制器。在 iOS 8 上由 Apple 审核。我们仅覆盖 supportedInterfaceOrientations。

值得注意的是,我们发现 Beta 3、4 和 5 之间存在很多差异。最终我们不得不等待 GM 齐心协力为 iOS 8 更新我们的应用。

在 iOS 8 中您需要非常小心,以确保您不会这样做:

[self dismissViewControllerAnimated:YES completion:nil]
[self presentViewController:vc animated:YES completion:nil]

如果传出的 vc 是纵向的,而传入的 vc 是横向的,则某些视图框架最终会变得非常混乱。而是在关闭调用的完成块中显示传入的 vc。

[self dismissViewControllerAnimated:YES completion:^{
    [self presentViewController:vc animated:YES completion:nil]
}];

【讨论】:

  • 什么是vc?原始呈现视图控制器?
  • Vc 是您要展示的新控制器。如果它是最初的展示 vc,你会尝试在自己身上展示自己
【解决方案5】:

这实际上更简单,无需任何附加属性即可完成(这里以AVPlayerViewController 为例):

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        if ([self.window.rootViewController.presentedViewController isKindOfClass: [AVPlayerViewController class]])
            return self.window.rootViewController.presentedViewController.isBeingDismissed ? 
            UIInterfaceOrientationMaskPortrait : UIInterfaceOrientationMaskAll;
        else
            return UIInterfaceOrientationMaskPortrait;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

【讨论】:

    【解决方案6】:

    @ZaEeM ZaFaR 提供的真棒问题和真棒答案!将他的回答与this 结合起来,我找到了一个很棒且更通用的解决方案。

    第一个答案的缺点是您必须在每个允许旋转的视图控制器中管理变量isPresented。此外,您必须为每个允许旋转的 vc 扩展检查并强制转换为 supportedInterfaceOrientationsForWindow

    第二个答案的缺点是它不起作用;它还会在关闭呈现的 vc 时旋转呈现的 vc。

    此解决方案允许在您放置 canRotate(){} 的所有 vc 中进行旋转,并且不旋转呈现的 vc。

    斯威夫特 3:
    在 AppDelegate.swift 中:

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        if let rootViewController = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController) {
            if (rootViewController.responds(to: Selector(("canRotate")))) {
                // Unlock landscape view orientations for this view controller if it is not currently being dismissed
                if !rootViewController.isBeingDismissed{
                    return .allButUpsideDown
                }
            }
        }
        
        // Only allow portrait (standard behaviour)
        return .portrait
    }
    
    private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
        if (rootViewController == nil) {
            return nil
        }
        if (rootViewController.isKind(of: UITabBarController.self)) {
            return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
        } else if (rootViewController.isKind(of: UINavigationController.self)) {
            return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
        } else if (rootViewController.presentedViewController != nil) {
            return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
        }
        return rootViewController
    }
    

    在每个应该允许旋转的视图控制器中:

    func canRotate(){}
    

    【讨论】:

    • 这非常好用。 Swift 4 用户唯一的“陷阱”是他们需要为 canRotate() 函数包含 @objc,即:@objc func canRotate(){}
    【解决方案7】:

    在根视图控制器中,尝试添加:

    - (NSUInteger)supportedInterfaceOrientations
    {
        return UIInterfaceOrientationMaskPortrait;
    }
    

    为我工作。

    【讨论】:

      【解决方案8】:

      Swift 3.0 或以上, 只需检查呈现的视图控制器的“isBeingDismissed”属性。 下面是示例代码,这将在呈现的视图控制器关闭后立即将呈现的视图控制器旋转到纵向模式。

      func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
      if let rootViewController = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController)
      {
        if rootViewController.canRotateVC == true
        {
          if baseVC.isBeingDismissed == false
          {
            return .allButUpsideDown
          }
        }
      }
      
        return .portrait}
      

      您可以通过以下代码获取topController:

        private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController?{
      if (rootViewController == nil) { return nil }if (rootViewController.isKind(of: (UITabBarController).self))
      {
        return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
      }
      else if (rootViewController.isKind(of:(UINavigationController).self))
      {
        return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
      }
      else if (rootViewController.presentedViewController != nil)
      {
        return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
      }
      return rootViewController }
      

      【讨论】:

      • 我试过你的方法,不幸的是方法 application:supportedInterfaceOrientationsForWindow: 在呈现的视图控制器被关闭时根本没有被调用。
      【解决方案9】:

      我遇到了同样的问题,终于找到了在另一个 UIWindow 中打开模态视图控制器的解决方案,并且运行顺利。

      堆栈 - iOS8 - prevent rotation on presenting viewController

      对于代码: https://github.com/OrenRosen/ModalInWindow

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多