获取最顶层的 UIViewController

251

我似乎无法在没有访问UINavigationController的情况下获得最顶层的UIViewController。以下是我的代码:

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)

然而,它似乎没有做任何事情。 keyWindowrootViewController 似乎也是非空值,因此可选链不应该是一个问题。

注意: 像这样做是一个坏主意。 它会破坏MVC模式。


这里有一个可用的替代方案 https://dev59.com/moTba4cB1Zd3GeqP41pp#39994115 - iDevAmit
29个回答

2
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {
            return topViewController(controller: presented)
        }
        return controller
    }

1

试试这个

let topVisibleVC = UIApplication.shared.keyWindow?.rootViewController?.visibleViewController

1
  var topViewController: UIViewController? {
        guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
        while let presentedViewController = topViewController.presentedViewController {
            topViewController = presentedViewController
        }
        return topViewController
    }

1

你把代码放在哪里了?

我在我的演示中尝试了你的代码,发现如果你把代码放在

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

将会失败,因为关键窗口尚未设置。

但是我将你的代码放在某些视图控制器中。

override func viewDidLoad() {

它只是起作用。


它不在didFinishLaunchingWithOptions中。我只需要用于各种调试目的。 - Zoyt

1
在非常罕见的情况下,使用自定义转场时,最顶层的视图控制器不在导航栏堆栈、选项卡控制器或被呈现,但它的视图被插入到关键窗口的子视图的顶部。
在这种情况下,需要检查UIApplication.shared.keyWindow.subviews.last == self.view以确定当前视图控制器是否是最顶层的。

1
任何寻找Swift 5/iOS 13+解决方案的人(自iOS 13以来keywindow已弃用)
extension UIApplication {

    class func getTopMostViewController() -> UIViewController? {
        let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
        if var topController = keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            return topController
        } else {
            return nil
        }
    }
}

我该如何使用它? - Chris Comas
只需在您的ViewController中这样调用它:UIApplication.getTopMostViewController()。@ChrisComas - Virendra

0

对我来说最好的解决方案是使用一个带有函数的扩展。创建一个 Swift 文件并添加这个扩展。

首先是 UIWindow 扩展:

public extension UIWindow {
    var visibleViewController: UIViewController? {
        return UIWindow.visibleVC(vc: self.rootViewController)
    }

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

在那个文件里添加函数。
func visibleViewController() -> UIViewController? {
    let appDelegate = UIApplication.shared.delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}

如果你想使用它,你可以在任何地方调用它。 例如

  override func viewDidLoad() {
    super.viewDidLoad()
      if let topVC = visibleViewController() {
             //show some label or text field 
    }
}

文件代码就像这样

import UIKit

public extension UIWindow {
    var visibleViewController: UIViewController? {
        return UIWindow.visibleVC(vc: self.rootViewController)
    }

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

func visibleViewController() -> UIViewController? {
    let appDelegate = UIApplication.shared.delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}

0
extension UIViewController {
    func topMostViewController() -> UIViewController {
        if self.presentedViewController == nil {
            return self
        }
        if let navigation = self.presentedViewController as? UINavigationController {
            return navigation.visibleViewController.topMostViewController()
        }
        if let tab = self.presentedViewController as? UITabBarController {
            if let selectedTab = tab.selectedViewController {
                return selectedTab.topMostViewController()
            }
            return tab.topMostViewController()
        }
        return self.presentedViewController!.topMostViewController()
    }
}

extension UIApplication {
    func topMostViewController() -> UIViewController? {
        return self.keyWindow?.rootViewController?.topMostViewController()
    }
}

-2

最简单的方法是从视图控制器堆栈中取出最后一个:

if let viewController: UIViewController = navigationController.viewControllers.last {
    // This `viewController` is on the top.
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接