我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。

基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)

或者,如果找不到,有没有可能找到最高处的风景?


当前回答

我最近在我的一个项目中遇到了这种情况,当网络状态发生变化时,需要显示一个通知视图,无论显示的控制器是什么,无论类型是什么(UINavigationController,经典控制器或自定义视图控制器)。

所以我刚刚发布了我的代码,这很简单,实际上是基于一个协议,所以它对每种类型的容器控制器都是灵活的。 它似乎与上一个答案有关,但方式更加灵活。

你可以从这里获取代码:PPTopMostController

并得到最顶级的控制器使用

UIViewController *c = [UIViewController topMostController];

其他回答

这是对Eric的回答的改进:

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

UIViewController *topMostController() {
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

UIViewController *cont是一个辅助函数。

现在你所需要做的就是调用topMostController()和最顶端的UIViewController应该被返回!

我最近在我的一个项目中遇到了这种情况,当网络状态发生变化时,需要显示一个通知视图,无论显示的控制器是什么,无论类型是什么(UINavigationController,经典控制器或自定义视图控制器)。

所以我刚刚发布了我的代码,这很简单,实际上是基于一个协议,所以它对每种类型的容器控制器都是灵活的。 它似乎与上一个答案有关,但方式更加灵活。

你可以从这里获取代码:PPTopMostController

并得到最顶级的控制器使用

UIViewController *c = [UIViewController topMostController];

一个完整的非递归版本,照顾到不同的场景:

视图控制器正在呈现另一个视图 视图控制器是一个UINavigationController 视图控制器是一个UITabBarController

objective - c

 UIViewController *topViewController = self.window.rootViewController;
 while (true)
 {
     if (topViewController.presentedViewController) {
         topViewController = topViewController.presentedViewController;
     } else if ([topViewController isKindOfClass:[UINavigationController class]]) {
         UINavigationController *nav = (UINavigationController *)topViewController;
         topViewController = nav.topViewController;
     } else if ([topViewController isKindOfClass:[UITabBarController class]]) {
         UITabBarController *tab = (UITabBarController *)topViewController;
         topViewController = tab.selectedViewController;
     } else {
         break;
     }
 }

斯威夫特 4+

extension UIWindow {
    func topViewController() -> UIViewController? {
        var top = self.rootViewController
        while true {
            if let presented = top?.presentedViewController {
                top = presented
            } else if let nav = top as? UINavigationController {
                top = nav.visibleViewController
            } else if let tab = top as? UITabBarController {
                top = tab.selectedViewController
            } else {
                break
            }
        }
        return top
    }
}

你应该使用:

[UIApplication sharedApplication].window.rootViewController;

当[UIApplication sharedApplication]上有一个uiactionsheet时。keyWindow,这是不正确的使用keyWindow在这个答案中提到。

简单的扩展UIApplication在Swift:

注意:

它关心UITabBarController中的moreNavigationController

extension UIApplication {

    class func topViewController(baseViewController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let navigationController = baseViewController as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }

        if let tabBarViewController = baseViewController as? UITabBarController {

            let moreNavigationController = tabBarViewController.moreNavigationController

            if let topViewController = moreNavigationController.topViewController where topViewController.view.window != nil {
                return topViewController(topViewController)
            } else if let selectedViewController = tabBarViewController.selectedViewController {
                return topViewController(selectedViewController)
            }
        }

        if let splitViewController = baseViewController as? UISplitViewController where splitViewController.viewControllers.count == 1 {
            return topViewController(splitViewController.viewControllers[0])
        }

        if let presentedViewController = baseViewController?.presentedViewController {
            return topViewController(presentedViewController)
        }

        return baseViewController
    }
}

简单的用法:

if let topViewController = UIApplication.topViewController() {
    //do sth with top view controller
}