仅在一个ViewController中旋转

时间:2015-01-30 19:29:25

标签: ios xcode swift uiviewcontroller rotation

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

14 个答案:

答案 0 :(得分:29)

我建议您在supportedInterfaceOrientationsForWindow中使用appDelegate以仅允许在该特定视图控制器中轮换,例如:

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

请注意,如果在转到纵向屏幕之前该特定视图控件处于横向状态,则另一个视图仍将以横向打开。为了避免这种情况,我建议在该视图处于横向状态时禁止转换。

Swift 4

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    if let navigationController = self.window?.rootViewController as? UINavigationController {
        if navigationController.visibleViewController is DetailsTableViewController {
            return UIInterfaceOrientationMask.all
        } else {
            return UIInterfaceOrientationMask.portrait
        }
    }
    return UIInterfaceOrientationMask.portrait
}

答案 1 :(得分:7)

您也可以以面向协议的方式执行此操作。 只需创建协议

protocol CanRotate {

}

在更多" swifty"中添加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
}

答案 2 :(得分:3)

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

此方法应覆盖故事板设置。

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

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

答案 3 :(得分:3)

这适用于 Swift 3和Swift 4 。您可以在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
}

您可以在原帖中了解更多信息:http://www.jairobjunior.com/blog/2016/03/05/how-to-rotate-only-one-view-controller-to-landscape-in-ios-slash-swift/

答案 4 :(得分:2)

我刚遇到一个非常类似的问题,我想在纵向和横向模式下呈现视频播放器,而应用程序的其余部分仅限于纵向。我的主要问题是,当我在横向模式中解除视频vc时,呈现的vc仅在横向模式中短暂出现 正如在@Lyndsey Scott's answer的评论中指出的那样,这可以通过在横向模式下禁止转换来规避,但通过合并thisthis,我发现了更好,更通用的解决方案(IMO)。此解决方案允许在放置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(){}

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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 :(得分:1)

有时,当您使用自定义导航流(可能会变得非常复杂)时,上述解决方案可能并不总是有效。此外,如果您有多个需要支持多种方向的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
    }

然后在ViewController中要进行多个导航的操作会更新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")
}

希望这会有所帮助!

答案 8 :(得分:1)

我想每个人的想法都写出了最优雅的方法。

结果:

    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 {
}

答案 9 :(得分:1)

解决方案 Swift 5.1

在应用委托中实现这个方法

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
    }
}

答案 10 :(得分:0)

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

var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { get }

重写此UIViewController method帮助我完成了我需要的工作。

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

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return UIInterfaceOrientation.landscapeLeft }

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

enter image description here

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

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

答案 11 :(得分:0)

雨燕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
        }
    }

答案 12 :(得分:0)

使用Marker协议的Swift 5

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

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
    }
}

答案 13 :(得分:-1)

这些答案都不对我有用。从根本上讲,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