簡體   English   中英

僅在一個 ViewController 中旋轉

[英]Rotation only in one ViewController

我正在嘗試旋轉一個視圖,而所有其他視圖 (5) 都固定為縱向。 原因是在那個視圖中,我希望用戶觀看他之前保存的圖片。 我想這是可能的,但到目前為止我無法弄清楚如何實現這一目標。 任何人都可以幫助或給我一個提示嗎? 我正在用在 iOS8 上運行的 Swift 編程

我建議在您的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)
}

您也可以以面向協議的方式進行。 只需創建協議

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
}

這適用於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

有時,當您使用自定義導航流(可能會變得非常復雜)時,上述解決方案可能並不總是有效。 此外,如果您有多個需要支持多個方向的 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")
}

希望這可以幫助!

更新:

您可能只想向您的Orientation Support Manager類添加一個static func

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

然后,您可以在需要將方向重新設置為縱向時調用此函數。 這也將更新靜態landscapeSupported值:

OSM.setOrientation(.portrait)

在要以橫向和縱向模式顯示的 ViewController 中使用shouldAutorotatesupportedInterfaceOrientations方法:

此方法應覆蓋故事板設置。

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

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

我剛剛遇到了一個非常相似的問題,我想以縱向和橫向模式呈現視頻播放器,而應用程序的其余部分僅是縱向的。 我的主要問題是,當我以橫向模式關閉視頻 vc 時,呈現的 vc 只是短暫地處於橫向模式。
正如對@Lyndsey Scott 的回答的評論中指出的那樣,這可以通過在橫向模式下禁止轉換來規避,但是通過將這個這個結合起來,我找到了一個更好、更通用的解決方案(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(){}

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

根據每個人的想法,我寫了我認為最優雅的方式。

結果:

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

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

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

解決方案 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
    }
}

只是想把我的解決方案分享給一個花了太多時間在應用程序中旋轉一個視圖控制器的人:

var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { get }

覆蓋這個 UIViewController 方法幫助我做我需要的。

  1. 在要旋轉的視圖控制器上,為橫向向左旋轉執行此操作:

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return UIInterfaceOrientation.landscapeLeft }

  1. 確保從項目設置中啟用在所需方向上的旋轉:

在此處輸入圖片說明

  1. 並將其添加到AppDelegate以禁用其他屏幕的旋轉:

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

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

這些答案都不適合我。 從根本上說,AppDelegate 的方法不允許指定哪個 viewController。 因此,要么 topMost ViewController 是可旋轉的,在這種情況下,整個視圖控制器層次結構都會旋轉,要么什么都不旋轉。

但是,我確實在Child View Controller to Rotate 中找到了一個有希望的答案, 而父視圖控制器則沒有

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM