我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。
基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)
或者,如果找不到,有没有可能找到最高处的风景?
我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。
基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)
或者,如果找不到,有没有可能找到最高处的风景?
当前回答
简单的扩展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
}
其他回答
我认为Rajesh的解决方案几乎是完美的,但我认为从上到下遍历子视图更好,我改为如下:
+ (UIViewController *)topViewController:(UIViewController *)viewController{
if (viewController.presentedViewController)
{
UIViewController *presentedViewController = viewController.presentedViewController;
return [self topViewController:presentedViewController];
}
else if ([viewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)viewController;
return [self topViewController:tabBarController.selectedViewController];
}
else if ([viewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navController = (UINavigationController *)viewController;
return [self topViewController:navController.visibleViewController];
}
// Handling UIViewController's added as subviews to some other views.
else {
NSInteger subCount = [viewController.view subviews].count - 1;
for (NSInteger index = subCount; index >=0 ; --index)
{
UIView *view = [[viewController.view subviews] objectAtIndex:index];
id subViewController = [view nextResponder]; // Key property which most of us are unaware of / rarely use.
if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
{
return [self topViewController:subViewController];
}
}
return viewController;
}
}
我认为你需要一个公认的答案和@fishstix的组合
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
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
}
这个解决方案是最完整的。它考虑到: UINavigationController UIPageViewController UITabBarController 最上面的视图控制器来自顶部视图控制器
以Swift 3为例。
有3个重载
//Get the topmost view controller for the current application.
public func MGGetTopMostViewController() -> UIViewController? {
if let currentWindow:UIWindow = UIApplication.shared.keyWindow {
return MGGetTopMostViewController(fromWindow: currentWindow)
}
return nil
}
//Gets the topmost view controller from a specific window.
public func MGGetTopMostViewController(fromWindow window:UIWindow) -> UIViewController? {
if let rootViewController:UIViewController = window.rootViewController
{
return MGGetTopMostViewController(fromViewController: rootViewController)
}
return nil
}
//Gets the topmost view controller starting from a specific UIViewController
//Pass the rootViewController into this to get the apps top most view controller
public func MGGetTopMostViewController(fromViewController viewController:UIViewController) -> UIViewController {
//UINavigationController
if let navigationViewController:UINavigationController = viewController as? UINavigationController {
let viewControllers:[UIViewController] = navigationViewController.viewControllers
if navigationViewController.viewControllers.count >= 1 {
return MGGetTopMostViewController(fromViewController: viewControllers[viewControllers.count - 1])
}
}
//UIPageViewController
if let pageViewController:UIPageViewController = viewController as? UIPageViewController {
if let viewControllers:[UIViewController] = pageViewController.viewControllers {
if viewControllers.count >= 1 {
return MGGetTopMostViewController(fromViewController: viewControllers[0])
}
}
}
//UITabViewController
if let tabBarController:UITabBarController = viewController as? UITabBarController {
if let selectedViewController:UIViewController = tabBarController.selectedViewController {
return MGGetTopMostViewController(fromViewController: selectedViewController)
}
}
//Lastly, Attempt to get the topmost presented view controller
var presentedViewController:UIViewController! = viewController.presentedViewController
var nextPresentedViewController:UIViewController! = presentedViewController?.presentedViewController
//If there is a presented view controller, get the top most prensentedViewController and return it.
if presentedViewController != nil {
while nextPresentedViewController != nil {
//Set the presented view controller as the next one.
presentedViewController = nextPresentedViewController
//Attempt to get the next presented view controller
nextPresentedViewController = presentedViewController.presentedViewController
}
return presentedViewController
}
//If there is no topmost presented view controller, return the view controller itself.
return viewController
}
@implementation UIWindow (Extensions) - (UIViewController*) topMostController { UIViewController *topController = [self rootViewController]; while (topController.presentedViewController) { topController = topController.presentedViewController; } return topController; } @end
伟大的解决方案在Swift,实现在AppDelegate
func getTopViewController()->UIViewController{
return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
if rootViewController is UITabBarController{
let tabBarController = rootViewController as! UITabBarController
return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
}
if rootViewController is UINavigationController{
let navBarController = rootViewController as! UINavigationController
return topViewControllerWithRootViewController(navBarController.visibleViewController)
}
if let presentedViewController = rootViewController.presentedViewController {
return topViewControllerWithRootViewController(presentedViewController)
}
return rootViewController
}