【问题标题】:Rotation only in one ViewController仅在一个 ViewController 中旋转
【发布时间】:2015-03-30 08:42:17
【问题描述】:

我正在尝试旋转一个视图,而所有其他视图 (5) 都固定为纵向。原因是在那个视图中,我希望用户观看他之前保存的图片。我想这是可能的,但到目前为止我无法弄清楚如何实现这一目标。任何人都可以帮助或给我一个提示吗? 我正在使用在 iOS8 上运行的 Swift 进行编程

【问题讨论】:

  • 当您说“旋转”时,您的意思是您希望视图能够在横向和纵向之间旋转,或者您只想将其锁定为横向,而其他视图为纵向?
  • 是的,你是对的,因为有些图片是风景,有些是肖像,我希望视图旋转。但只有在这一个视图中,所有其他视图都需要固定为纵向。现在我将所有视图硬编码为纵向,但想更改那个视图。

标签: ios xcode swift uiviewcontroller rotation


【解决方案1】:

在要以横向和纵向模式显示的 ViewController 中使用 shouldAutorotatesupportedInterfaceOrientations 方法:

此方法应覆盖情节提要设置。

override func shouldAutorotate() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> Int {
    return UIInterfaceOrientation.Portrait.rawValue | UIInterfaceOrientation.LandscapeLeft.rawValue | UIInterfaceOrientation.LandscapeRight.rawValue
}

【讨论】:

  • 嘿,Christian,这个答案似乎只有在您将UINavigationController 子类化并分别为supportedInterfaceOrientations()shouldAutorotate() 函数返回self.topViewController.supportedInterfaceOrientations()self.topViewController.shouldAutorotate() 时才有效。
【解决方案2】:

我建议在您的 appDelegate 中使用 supportedInterfaceOrientationsForWindow 以仅允许在该特定视图控制器中进行旋转,例如:

斯威夫特 4/斯威夫特 5

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {

    // Make sure the root controller has been set
    // (won't initially be set when the app is launched)
    if let navigationController = self.window?.rootViewController as? UINavigationController {

        // If the visible view controller is the
        // view controller you'd like to rotate, allow
        // that window to support all orientations
        if navigationController.visibleViewController is SpecificViewController {
            return UIInterfaceOrientationMask.all
        } 

        // Else only allow the window to support portrait orientation
        else {
            return UIInterfaceOrientationMask.portrait
        }
    }

    // If the root view controller hasn't been set yet, just
    // return anything
    return UIInterfaceOrientationMask.portrait
}

请注意,如果 SpecificViewController 在进入纵向屏幕之前处于横向状态,则其他视图仍将以横向方式打开。为了避免这种情况,我建议在该视图处于横向时禁止转换。


斯威夫特 3

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

    // Make sure the root controller has been set
    // (won't initially be set when the app is launched)
    if let navigationController = self.window?.rootViewController as? UINavigationController {

        // If the visible view controller is the
        // view controller you'd like to rotate, allow
        // that window to support all orientations
        if navigationController.visibleViewController is SpecificViewController  {
            return Int(UIInterfaceOrientationMask.All.rawValue)
        }

        // Else only allow the window to support portrait orientation
        else {
            return Int(UIInterfaceOrientationMask.Portrait.rawValue)
        }
    }

    // If the root view controller hasn't been set yet, just
    // return anything
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

【讨论】:

  • @ArminScheithauer 没问题 :) 老实说,我不相信 Christian 的解决方案会在所有情况下都有效,因为我在测试他的代码时无法产生预期的结果......跨度>
  • @ArminScheithauer 要获得 Christian 的工作答案,您必须继承 UINavigationViewController 并使用 UInterfaceOrientationMask 而不是 UInterfaceOrientation 等等...
  • @LyndseyScott 这是什么意思“当视图处于横向时不允许转换。”你能详细解释一下吗。谢谢
【解决方案3】:

斯威夫特 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
                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
    }

然后:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }


    override func viewWillDisappear(_ animated : Bool) {
        super.viewWillDisappear(animated)

        if (self.isMovingFromParentViewController) {
            UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
        }
    }

    func canRotate() -> Void {}

}

http://www.jairobjunior.com/blog/2016/03/05/how-to-rotate-only-one-view-controller-to-landscape-in-ios-slash-swift/

【讨论】:

    【解决方案4】:

    我刚刚遇到了一个非常相似的问题,我想以纵向和横向模式呈现视频播放器,而应用程序的其余部分仅是纵向的。我的主要问题是,当我以横向模式关闭视频 vc 时,呈现的 vc 只是短暂地处于横向模式。
    正如@Lyndsey Scott's answer 的评论中所指出的,这可以通过在横向模式下禁止转换来规避,但通过结合thisthis,我找到了一个更好、更通用的解决方案(IMO)。此解决方案允许在您放置 canRotate(){} 的所有 vc 中旋转,并且不旋转呈现的 vc。

    Swift 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(){}
    

    【讨论】:

      【解决方案5】:

      您也可以以面向协议的方式进行。 只需创建协议

      protocol CanRotate {
      
      }
      

      以更“迅速”的方式在 AppDelegate 中添加相同的 2 个方法

      func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
          if topViewController(in: window?.rootViewController) is CanRotate {
              return .allButUpsideDown
          } else {
              return .portrait
          }
      }
      
      
      func topViewController(in rootViewController: UIViewController?) -> UIViewController? {
          guard let rootViewController = rootViewController else {
              return nil
          }
      
          if let tabBarController = rootViewController as? UITabBarController {
              return topViewController(in: tabBarController.selectedViewController)
          } else if let navigationController = rootViewController as? UINavigationController {
              return topViewController(in: navigationController.visibleViewController)
          } else if let presentedViewController = rootViewController.presentedViewController {
              return topViewController(in: presentedViewController)
          }
          return rootViewController
      }
      

      并且在您想要不同行为的每个 ViewController 中,只需在类的定义中添加协议名称即可。

      class ViewController: UIViewController, CanRotate {}
      

      如果你想要任何特定的组合,你可以在协议中添加一个变量来覆盖

      protocol CanRotate {
          var supportedInterfaceOrientations: UIInterfaceOrientationMask
      }
      

      【讨论】:

      • 无法对框架内的视图控制器执行此操作,因为框架没有 AppDelegate 文件
      【解决方案6】:

      SWIFT 4

      对于UITabBarController,我们可以在AppDelegate.swift中使用这行代码吗?

      func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
          if let tabBarController = window?.rootViewController as? UITabBarController {
              if let tabBarViewControllers = tabBarController.viewControllers {
                  if let projectsNavigationController = tabBarViewControllers[1] as? UINavigationController {
                      if projectsNavigationController.visibleViewController is PickerViewController //use here your own ViewController class name {
                          return .all
                      }
                  }
              }
          }
          return .portrait
      }
      

      【讨论】:

        【解决方案7】:

        这适用于 Swift 4 和 Swift 5。您可以在 AppDelegate.swift 中使用以下代码:

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

        然后您可以通过覆盖 shouldAutorotate 来使自定义 UIViewController 旋转

        【讨论】:

        • 我还添加到viewWillDisappear if (self.isMovingFromParent) { UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation") } 所以,呈现的vc不会在pop上旋转。
        • 我已经完成了所有这些更改,但对我来说没有任何改变,还有其他建议吗?
        【解决方案8】:

        有时,当您使用自定义导航流程(可能会变得非常复杂)时,上述解决方案可能并不总是有效。此外,如果您有多个 ViewController 需要支持多个方向,这可能会变得非常乏味。

        这是我找到的一个相当快速的解决方案。定义一个类 OrientationManager 并使用它来更新 AppDelegate 中支持的方向:

        class OrientationManager {
            static var landscapeSupported: Bool = false
        }
        

        然后在 AppDelegate 中为特定情况输入您想要的方向:

        func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
                if OrientationManager.landscapeSupported {
                    return .allButUpsideDown
                }
                return .portrait
            }
        

        然后在您想要多个导航的 ViewControllers 中更新 OrientationManager:

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            OrientationManager.landscapeSupported = true
        }
        

        另外,当你要退出这个 ViewController 时不要忘记再次更新它:

        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            OrientationManager.landscapeSupported = false
            //The code below will automatically rotate your device's orientation when you exit this ViewController
            let orientationValue = UIInterfaceOrientation.portrait.rawValue
            UIDevice.current.setValue(orientationValue, forKey: "orientation")
        }
        

        希望这会有所帮助!

        更新:

        您可能只想将static func 添加到您的Orientation Support Manager 类中:

            static func setOrientation(_ orientation: UIInterfaceOrientation) {
                let orientationValue = orientation.rawValue
                UIDevice.current.setValue(orientationValue, forKey: "orientation")
                landscapeSupported = orientation.isLandscape
            }
        

        然后,您可以在需要将方向设置回纵向时调用此函数。这也会更新静态的landscapeSupported 值:

        OSM.setOrientation(.portrait)
        

        【讨论】:

          【解决方案9】:

          只是想与在应用程序中旋转一个视图控制器花费太多时间的人分享我的解决方案:

          var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { get }
          

          覆盖这个 UIViewController method 帮助我做我需要的事情。

          1. 在要旋转的视图控制器上,为横向左旋转执行此操作:

          override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return UIInterfaceOrientation.landscapeLeft }

          1. 确保从项目设置中启用所需方向的旋转:

          1. 并将其添加到 AppDelegate 以禁用其他屏幕的旋转:

          func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return .portrait }

          【讨论】:

          • 我已经尝试过您的解决方案。不幸的是,它不起作用。
          【解决方案10】:

          斯威夫特 5

          另一个答案,这个答案涵盖了 isBeingDismissed 案例。

          在 AppDelegate 中:

              func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
                  if
                      let vvc = navigationController?.visibleViewController,
                      vvc is YOURViewControllerClassName &&
                      !vvc.isBeingDismissed
                  {
                      return UIInterfaceOrientationMask.landscape
                  } else {
                      return UIInterfaceOrientationMask.portrait
                  }
              }
          

          【讨论】:

          • 请问navigationController?来自哪里?
          【解决方案11】:

          Swift 5 使用 Marker 协议

          这里有几个答案的组合版本,我认为这是一个更易读/优雅的实现。 (源自此处较早的答案,不是我的原创作品!)

          protocol RotatableViewController {
              // No content necessary, marker protocol
          }
          
          class MyViewController: UIViewController, RotatableViewController {
              // normal content... nothing more required
          }
          
          extension AppDelegate {
          
              func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
                  guard
                      let rootVc = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController),
                      rootVc.isBeingDismissed == false,
                      let _ = rootVc as? RotatableViewController
                  else {
                      return .portrait  // Some condition not met, so default answer for app
                  }
                  // Conditions met, is rotatable:
                  return .allButUpsideDown
              }
          
          
              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
              }
          }
          

          【讨论】:

          • 如果 MyRotatbleVC 出现并且在横向模式下被关闭时,之前的 VC 将处于横向模式,而不是保持纵向模式。
          【解决方案12】:

          这些答案都不适合我。从根本上说,AppDelegate 的方法不允许指定哪个 viewController。所以要么 topMost ViewController 是可旋转的,在这种情况下,整个视图控制器层次结构都会被旋转,或者什么都不会旋转。

          不过,我确实在Child View Controller to Rotate While Parent View Controller Does Not 中找到了一个有希望的答案

          引用https://developer.apple.com/library/archive/qa/qa1890/_index.html

          【讨论】:

            【解决方案13】:

            结合大家的想法,我写出了我认为最优雅的方式。

            结果:

                func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
                    return (UIApplication.getTopViewController() as? Rotatable == nil) ? .portrait : .allButUpsideDown
                }
            

            将此扩展添加到您的项目中,它不仅对这个有用:

            extension UIApplication {
            
                class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
                    if let nav = base as? UINavigationController {
                        return getTopViewController(base: nav.visibleViewController)
                    }
            
                    if let tab = base as? UITabBarController {
                        if let selected = tab.selectedViewController {
                            return getTopViewController(base: selected)
                        }
                    }
            
                    if let presented = base?.presentedViewController {
                        return getTopViewController(base: presented)
                    }
            
                    return base
                }
            
            }
            

            创建协议:

            protocol Rotatable {}
            

            并实现它:

            class ViewController: UIViewController, Rotatable {
            }
            

            【讨论】:

            • 太棒了!谢谢!
            • 非常感谢。你的回答奏效了。这两天我一直在寻找解决方案。
            【解决方案14】:

            解决方案 Swift 5.1

            在 App 委托中实现此方法

            func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
                 if let topController = UIApplication.topViewController() {
                 
                    if topController.isKind(of: YourSpecificViewController.self) {
                        return .all
                    }
                    return .portrait
                 }
                return .portrait
            }
            

            然后添加这个扩展以获得最高的ViewController

            extension UIApplication {
                class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
                
                    if let nav = base as? UINavigationController {
                        return topViewController(base: nav.visibleViewController)
                        
                    } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
                        return topViewController(base: selected)
                        
                    } else if let presented = base?.presentedViewController {
                        return topViewController(base: presented)
                    }
                    return base
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2014-12-16
              • 1970-01-01
              • 2012-09-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多