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

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

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


当前回答

Swift 4.2中一个简洁而全面的解决方案,考虑了UINavigationControllers, UITabBarControllers, presenting和子视图控制器:

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

用法:

let viewController = UIApplication.shared.topmostViewController()

其他回答

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

[UIApplication sharedApplication].keyWindow.rootViewController;

你需要在创建视图控制器后自己设置。

以下是对我有效的方法。

我发现有时候控制器在键窗口上是nil,因为键窗口是一些操作系统的东西,如警报等。

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

     if (topController == nil)
     {
         // The windows in the array are ordered from back to front by window level; thus,
         // the last window in the array is on top of all other app windows.
         for (UIWindow *aWndow in [[UIApplication sharedApplication].windows reverseObjectEnumerator])
         {
             topController = aWndow.rootViewController;
             if (topController)
                 break;
         }
     }

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

     return topController;
 }

Swift 4.2中一个简洁而全面的解决方案,考虑了UINavigationControllers, UITabBarControllers, presenting和子视图控制器:

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

用法:

let viewController = UIApplication.shared.topmostViewController()

以下是我的看法。感谢@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;
}

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

在任何地方都可以使用,

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)

或者像,

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(yourController,
                                         animated: true)

适合任何类,如UINavigationController, UITabBarController

享受吧!