iPhone - 如何找到最顶层的视图控制器

Hot*_*cks 240 objective-c uiviewcontroller uikit uiview ios

我现在遇到了几个案例,它们可以方便地找到"最顶层"的视图控制器(负责当前视图的控制器),但还没有找到办法.

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

或者,如果失败了,是否有可能找到最顶层的视图?

Eri*_*ric 417

我认为你需要一个接受的答案和@ fishstix的组合

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

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

    return topController;
}
Run Code Online (Sandbox Code Playgroud)

Swift 3.0+

func topMostController() -> UIViewController? {
    guard let window = UIApplication.shared.keyWindow, let rootViewController = window.rootViewController else {
        return nil
    }

    var topController = rootViewController

    while let newTopController = topController.presentedViewController {
        topController = newTopController
    }

    return topController
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个不完整的解决方案,因为它只遍历模态呈现的视图控制器的层次结构,而不是childViewControllers的层次结构(由UINavigationController,UITabBarController等使用). (31认同)
  • @algal:不是真的:UITabBarController,UINavigationController*已经是层次结构中最顶层的视图控制器.根据您对"最顶层控制器"*的要求,您可能根本不想遍历它们*并根据它们的内容进行操作.在我的情况下,它是在一切上呈现模态控制器,为此我需要获得UINaviationController或UITabBarController,**不是他们的内容**!! (11认同)
  • 此外,您可以检查`UINavigationController`并询问其`topViewController`,甚至检查`UITabBarController`并询问`selectedViewController`.这将为您提供当前对用户可见的视图控制器. (4认同)
  • 这是一个很好的方法来抽象出一个恢复到当前应用程序状态的模态视图控制器的呈现,在我的情况下,它是在应用程序超时后的密码重新进入屏幕.谢谢! (3认同)

kle*_*leo 151

要完成JonasG的答案(在遍历时遗漏了标签栏控制器),这是我返回当前可见视图控制器的版本:

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不包括`childViewControllers` (7认同)
  • 很好,是的,我忘记了TabBar控制器:P (2认同)

Wil*_*ith 79

iOS 4在UIWindow上引入了rootViewController属性:

[UIApplication sharedApplication].keyWindow.rootViewController;
Run Code Online (Sandbox Code Playgroud)

您需要在创建视图控制器后自己设置它.

  • 威尔伯,这将给你与op所要求的相反.rootViewController是基本视图控制器而不是最顶层的. (145认同)
  • 单词"top"用于视图控制器,即top_上的_visualy(如` - [UINavigationController topViewController]`).然后是单词"root",它是tree_的_root(如` - [UIWindow rootViewController]`. (47认同)
  • @ImpurestClub我无法在[文档](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIWindow_Class/index.html)中找到,Xcode似乎没有找到它. (13认同)
  • @adib不,它属于**UINavigationController** (4认同)
  • m4rkk:"最顶层"取决于您正在寻找的方向.新的控制器是添加到顶部(堆栈式)还是底部(树状)?在任何情况下,OP都提到导航控制器在顶部,这意味着向下增长的视图. (3认同)

Yuc*_*ong 47

一个完整的非递归版本,负责不同的场景:

  • 视图控制器正在呈现另一个视图
  • 视图控制器是一个 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;
     }
 }
Run Code Online (Sandbox Code Playgroud)

Swift 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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我将其命名为“ visibleViewController”,以明确其功能。 (2认同)

Var*_*una 27

使用扩展获取Swift的最顶层视图控制器

码:

extension UIViewController {
    @objc func topMostViewController() -> UIViewController {
        // Handling Modal views
        if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        // Handling UIViewController's added as subviews to some other views.
        else {
            for view in self.view.subviews
            {
                // Key property which most of us are unaware of / rarely use.
                if let subViewController = view.next {
                    if subViewController is UIViewController {
                        let viewController = subViewController as! UIViewController
                        return viewController.topMostViewController()
                    }
                }
            }
            return self
        }
    }
}

extension UITabBarController {
    override func topMostViewController() -> UIViewController {
        return self.selectedViewController!.topMostViewController()
    }
}

extension UINavigationController {
    override func topMostViewController() -> UIViewController {
        return self.visibleViewController!.topMostViewController()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()
Run Code Online (Sandbox Code Playgroud)


小智 24

为了完成Eric的答案(在浏览过程中遗漏了弹出窗口,导航控制器,tabbarcontrollers,视图控制器作为子视图添加到其他视图控制器),这是我返回当前可见视图控制器的版本:

================================================== ===================

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController {
    if ([viewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)viewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([viewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navContObj = (UINavigationController*)viewController;
        return [self topViewControllerWithRootViewController:navContObj.visibleViewController];
    } else if (viewController.presentedViewController && !viewController.presentedViewController.isBeingDismissed) {
        UIViewController* presentedViewController = viewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    }
    else {
        for (UIView *view in [viewController.view subviews])
        {
            id subViewController = [view nextResponder];
            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                if ([(UIViewController *)subViewController presentedViewController]  && ![subViewController presentedViewController].isBeingDismissed) {
                    return [self topViewControllerWithRootViewController:[(UIViewController *)subViewController presentedViewController]];
                }
            }
        }
        return viewController;
    }
}
Run Code Online (Sandbox Code Playgroud)

================================================== ===================

现在你需要做的就是获得最顶层的视图控制器,调用上面的方法如下:

UIViewController *topMostViewControllerObj = [self topViewController];
Run Code Online (Sandbox Code Playgroud)


Awe*_*e-o 21

这个答案包括childViewControllers并保持一个干净和可读的实现.

+ (UIViewController *)topViewController
{
    UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

    return [rootViewController topVisibleViewController];
}

- (UIViewController *)topVisibleViewController
{
    if ([self isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)self;
        return [tabBarController.selectedViewController topVisibleViewController];
    }
    else if ([self isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self;
        return [navigationController.visibleViewController topVisibleViewController];
    }
    else if (self.presentedViewController)
    {
        return [self.presentedViewController topVisibleViewController];
    }
    else if (self.childViewControllers.count > 0)
    {
        return [self.childViewControllers.lastObject topVisibleViewController];
    }

    return self;
}
Run Code Online (Sandbox Code Playgroud)


ipo*_*ima 13

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

所以我juste发布了我的代码,这很简单并且实际上基于协议,因此它对于每种类型的容器控制器都是灵活的.它似乎与最后的答案有关,但是以一种非常灵活的方式.

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

并获得最高控制器使用

UIViewController *c = [UIViewController topMostController];
Run Code Online (Sandbox Code Playgroud)


Jon*_*asG 11

这是对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;
}
Run Code Online (Sandbox Code Playgroud)

_topMostController(UIViewController *cont) 是一个辅助函数.

现在您需要做的就是调用topMostController()并返回最顶层的UIViewController!

  • 自1983年以来我会说.请记住,Objective-C包含C ...在C函数中包装ObjC代码是一种常见的做法,所以是的,这是Objective-C代码. (7认同)

小智 8

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}
Run Code Online (Sandbox Code Playgroud)


Bar*_*zyk 8

UIApplication在Swift中的简单扩展:

注意:

它在乎的moreNavigationControllerUITabBarController

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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法简单:

if let topViewController = UIApplication.topViewController() {
    //do sth with top view controller
}
Run Code Online (Sandbox Code Playgroud)


Fis*_*tix 7

@implementation UIWindow (Extensions)

- (UIViewController*) topMostController
{
    UIViewController *topController = [self rootViewController];

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

    return topController;
}

@end


Kam*_*han 7

这是我对此的看法.感谢@Stakenborg指出了跳过让UIAlertView成为最顶级控制器的方法

-(UIWindow *) returnWindowWithWindowLevelNormal
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for(UIWindow *topWindow in windows)
    {
        if (topWindow.windowLevel == UIWindowLevelNormal)
            return topWindow;
    }
    return [UIApplication sharedApplication].keyWindow;
}

-(UIViewController *) getTopMostController
{
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal)
    {
        topWindow = [self returnWindowWithWindowLevelNormal];
    }

    UIViewController *topController = topWindow.rootViewController;
    if(topController == nil)
    {
        topWindow = [UIApplication sharedApplication].delegate.window;
        if (topWindow.windowLevel != UIWindowLevelNormal)
        {
            topWindow = [self returnWindowWithWindowLevelNormal];
        }
        topController = topWindow.rootViewController;
    }

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

    if([topController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *nav = (UINavigationController*)topController;
        topController = [nav.viewControllers lastObject];

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

    return topController;
}
Run Code Online (Sandbox Code Playgroud)


Ash*_*hok 7

有关最新的Swift版本:
创建文件,请为其命名UIWindowExtension.swift并粘贴以下代码段:

import UIKit

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

    public static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
        if let nc = vc as? UINavigationController {
            return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
        } else if let tc = vc as? UITabBarController {
            return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
        } else {
            if let pvc = vc?.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(pvc)
            } else {
                return vc
            }
        }
    }
}

func getTopViewController() -> UIViewController? {
    let appDelegate = UIApplication.sharedApplication().delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

在任何地方使用它:

if let topVC = getTopViewController() {

}
Run Code Online (Sandbox Code Playgroud)


Sar*_*ith 6

斯威夫特 4.2 扩展


extension UIApplication {

    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
    }
}
Run Code Online (Sandbox Code Playgroud)

从任何地方使用它,例如,

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)
Run Code Online (Sandbox Code Playgroud)

或者像,

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(yourController,
                                         animated: true)
Run Code Online (Sandbox Code Playgroud)

适合任何类,如 UINavigationController、UITabBarController

享受!


nal*_*exn 5

Swift 4.2 中简洁而全面的解决方案,考虑了UINavigationControllersUITabBarControllers呈现视图控制器:

extension UIViewController {
  func topmostViewController() -> UIViewController {
    if let navigationVC = self as? UINavigationController,
      let topVC = navigationVC.topViewController {
      return topVC.topmostViewController()
    }
    if let tabBarVC = self as? UITabBarController,
      let selectedVC = tabBarVC.selectedViewController {
      return selectedVC.topmostViewController()
    }
    if let presentedVC = presentedViewController {
      return presentedVC.topmostViewController()
    }
    if let childVC = children.last {
      return childVC.topmostViewController()
    }
    return self
  }
}

extension UIApplication {
  func topmostViewController() -> UIViewController? {
    return keyWindow?.rootViewController?.topmostViewController()
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let viewController = UIApplication.shared.topmostViewController()
Run Code Online (Sandbox Code Playgroud)


Hit*_*esh 5

使用以下扩展名抓取当前可见的UIViewController。适用于Swift 4.0及更高版本

Swift 4.0及更高版本:

extension UIApplication {

    class func topViewController(_ viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = viewController as? UINavigationController {
            return topViewController(nav.visibleViewController)
        }
        if let tab = viewController as? UITabBarController {
            if let selected = tab.selectedViewController {
                return topViewController(selected)
            }
        }
        if let presented = viewController?.presentedViewController {
            return topViewController(presented)
        }
        return viewController
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用?

let objViewcontroller = UIApplication.topViewController()
Run Code Online (Sandbox Code Playgroud)