如何在iOS上找到最上方的视图控制器


253

现在,我遇到了两种情况,可以方便地找到“最顶层”视图控制器(负责当前视图的视图控制器),但还没有找到一种方法。

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

否则,是否有可能找到最顶层的视图?


所以你说这是不可能的。
热舔

@Daniel不,我是说您的代码似乎可以使用一些重新设计,因为您几乎不需要了解这一点。同样,“最高”的概念仅在某些情况下有效,甚至并非总是如此。
Dave DeLong

@丹尼尔我误读了你的问题。有很多假设和答案试图回答这一问题。这取决于您的视图控制器流程。@Wilbur的答案应该是追踪它的一个很好的起点。
Deepak Danduprolu 2011年

好吧,让我们将其简化为特定情况。如果我想编写UIAlertView的副本,该怎么办?请注意,它可以很好地运行,而无需将任何可寻址性传递给其他控制器或视图。
热舔

4
@Daniel:添加第二个UIWindow可以很好地用于类似警报视图的叠加层。
Wilbur Vandrsmith 2011年

Answers:


75

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

[UIApplication sharedApplication].keyWindow.rootViewController;

不过,在创建视图控制器之后,您需要自己进行设置。


155
威尔伯(Wilbur),这将为您提供操作要求的相反功能。rootViewController是基础视图控制器,而不是最顶层的控制器。
m4rkk 2012年

3
m4rkk:“最高”取决于您从哪个方向看。是否将新的控制器添加到顶部(类似栈)或底部(类似树)?无论如何,OP都将导航控制器放在最上面,这意味着向下看。
Wilbur Vandrsmith

50
单词“ top”用于视图控制器,在顶部可见(如-[UINavigationController topViewController])。再有就是字“根”,这是树的根(像-[UIWindow rootViewController]
Tricertops

13
我在文档中找不到@ImpurestClub ,Xcode似乎找不到它。
Drux

4
@adib不,它属于UINavigationController
David H

428

我认为您需要接受的答案和@fishstix的组合

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

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

    return topController;
}

迅捷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
}

4
另外,您可以检查UINavigationController并要求它topViewController,甚至可以检查UITabBarController并要求selectedViewController。这将为您提供用户当前可见的视图控制器。
Tricertops

33
这是一个不完整的解决方案,因为它仅遍历模态呈现的视图控制器的层次结构,而不遍历childViewControllers的层次结构(如UINavigationController,UITabBarController等所使用)。
algal

3
这是抽象出恢复到当前应用程序状态的模式视图控制器的呈现的好方法,在我的情况下,这是应用程序超时后的密码重新输入屏幕。谢谢!
erversteeg 2013年

11
@algal:不是真的:的UITabBarController,UINavigationController的已经在层次中最顶层视图控制器。取决于您要对“最高控制器”进行的操作,您可能根本不想遍历它们并摆弄它们的内容。就我而言,这是在所有内容之上都提供一个模态控制器,为此,我需要获取UINaviationController或UITabBarController,而不是它们的内容
Rick77

1
@ Rick77,如果这是真的,则您在此处埋下的一条小评论使其他答案中的大量复杂修改变得不必要。既然没有人提到这件事,我觉得我不得不请你确认这是真的。如果是的话,那么它是如此重要,以至于它本身应该成为一个答案。因为大多数其他答案都是尝试解决此问题的后空翻。您将挽救生命!
Le Mot Juiced 2015年

150

为了完成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;
    }
}

2
不错,是的,我忘记了TabBar控制器:P
JonasG,2013年

9
不包括childViewControllers
Awesome-o

看看我的回答下面这些由办案提高了上面的回答@kleo冷落如popovers,添加子视图的一些其他视图控制器在遍历视图控制器
拉杰什

如果使用return [self topViewControllerWithRootViewController:navigationController.visibleViewController] ;,则visibleViewController本身将返回显示的视图控制器(如果存在),即使它是UIAlertController。对于需要避免ui警报控制器的用户,请使用topViewController而不是visibleViewController
Johnykutty15

只是添加我的50美分-我正在努力使它在加载webView的viewcontroller中工作。.之所以无法使它工作,是因为视图仍未准备好(尚未完成加载)因此它是不可见的。这导致获取topViewContoller失败的情况,因为UINavigationController试图获取可见的ViewController,而尚无可见的ViewController。因此,如果有人遇到此问题,请在调用上述topViewController方法之前,确保视图已完成加载。
mbuster '16

52

完整的非递归版本,照顾不同的情况:

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

物镜

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

2
我将其命名visibleViewController以使其清楚。
Jonny

31

使用扩展获取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()
    }
}

用法:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()

很好-非常感谢您提供此解决方案。需要subviews'trick!再次非常感谢,您拯救了我的生活。
iKK

25

为了完成Eric的答案(遍历时遗漏了弹出窗口,导航控制器,tabbarcontroller,作为子视图添加到其他视图控制器的视图控制器的人),这是我返回当前可见的视图控制器的版本:

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

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

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

现在,要获得最高的视图控制器,您需要做的就是调用上述方法,如下所示:

UIViewController *topMostViewControllerObj = [self topViewController];

也缺少SplitViewController吗?
apinho

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

更新了一些代码,还通过最小化并再次还原来显示它是什么控制器。nik-kov-ios-developer.blogspot.ru/2016/12/...
聂KOV

嘿,来吧,您的“ topVisibleViewController”在哪里?
天堂

12

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

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

您可以在此处获取代码:PPTopMostController

并获得使用最多的控制器

UIViewController *c = [UIViewController topMostController];

10

这是对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;
}

_topMostController(UIViewController *cont) 是一个辅助功能。

现在您需要做的就是调用,topMostController()并且应该返回最上面的UIViewController!


7
从1983年开始,我会说。请记住,Objective-C包含C ...在C函数中包装ObjC代码是一种常见的做法,所以,是的,这是Objective-C代码。
JonasG 2014年

@JonasG嗨,乔纳斯,在什么情况下,您更喜欢将ObjC代码包装在C中?因为,有时我会看到这样的C函数,无法区分用法。用C包装代码是否可以提高性能?
OzBoz

1
@OzBoz在无法立即清除self应属于哪个类的情况下。
adib

8

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

您应该避免像getSomething:Objective-C中那样命名方法。这具有特殊含义(更多信息:cocoadevcentral.com/articles/000082.php),您在代码中不满足这些要求。
Vive 2015年

7
@implementation UIWindow(扩展名)

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

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

    返回topController;
}

@结束

我认为您不满足原始帖子中所述的条件。
2012年

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

我使用了此方法,但请注意,当显示的视图控制器不止一个时,它就会中断
Chuck Boris

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
}

可以在任何地方使用它:

if let topVC = getTopViewController() {

}

我不想过多地更改您的答案,但会提出一些建议。1.添加对UISplitViewController的支持。2.使用switch代替if else。3.不确定是否还需要静态函数,我想您可以在声明的第一个实例级别var中轻松完成此操作。4.最好不要创建太多的全局函数,但这只是一个问题。您可以使用一行代码来实现全局函数的效果:UIApplication.sharedApplication().delegate?.window?.visibleViewController
Jordan Smith

7

UIApplicationSwift中的简单扩展:

注意:

它在乎的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
    }
}

简单用法:

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

是是是-网上有很多解决方案可以找到topMostViewController,但是如果您的应用程序具有带有“更多”选项卡的选项卡栏,则您必须处理一些区别。
安迪·奥布塞克

7

使用以下扩展名抓取当前可见的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
    }
}

如何使用?

let objViewcontroller = UIApplication.topViewController()

不应该presentedViewControllerUINavigationControllerUITabBarController案例之前先进行此测试吗?否则,如果视图控制器是通过UINavigationController或模态呈现的UITabBarController,则即使它是可见的视图控制器也不会作为顶视图控制器返回。
德鲁

4

另一个Swift解决方案

func topController() -> UIViewController? {

    // recursive follow
    func follow(from:UIViewController?) -> UIViewController? {
        if let to = (from as? UITabBarController)?.selectedViewController {
            return follow(to)
        } else if let to = (from as? UINavigationController)?.visibleViewController {
            return follow(to)
        } else if let to = from?.presentedViewController {
            return follow(to)
        }
        return from
    }

    let root = UIApplication.sharedApplication().keyWindow?.rootViewController

    return follow(root)

}

4

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

请享用!


1
@BilalBakhrom说:“赞成。我认为您的回答是最好的。您不能直接调用topViewController()方法。UIApplication类是单例,请使用一个名为“ shared”的实例。” 在我投票拒绝的修改中。如果实际上是正确的,请单击此处进入菜单,您可以在其中批准该编辑。
wizzwizz4

3

这对我有用。

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

 + (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;
 }

3

在扩展@Eric的答案时,您需要注意keyWindow实际上是您想要的窗口。例如,如果您在敲击警报视图中的某些内容后尝试使用此方法,则keyWindow实际上将是警报的窗口,这无疑会给您造成问题。这是我在通过警报处理深层链接时在野外发生的事情,并导致SIGABRT出现NO STACK TRACE。总母狗要调试。

这是我现在使用的代码:

- (UIViewController *)getTopMostViewController {
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [UIApplication sharedApplication].windows;
        for(topWindow in windows)
        {
            if (topWindow.windowLevel == UIWindowLevelNormal)
                break;
        }
    }

    UIViewController *topViewController = topWindow.rootViewController;

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

    return topViewController;
}

随意将其与从该问题的其他答案中检索您喜欢的顶视图控制器的任何方式混合使用。


您是否发现这是一个完整的解决方案?因此,许多其他答案非常复杂,试图解释那么多极端情况。我希望这是真的,它是如此简单而优雅。
Le Mot Juiced 2015年

我从来没有问题。如果您没有对导航堆栈进行任何异常处理,这应该可以工作,否则其他一些解决方案可以处理更复杂的情况。
Stakenborg

3

另一种Swift解决方案:

static func topMostController() -> UIViewController {
    var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    while (topController?.presentedViewController != nil) {
        topController = topController?.presentedViewController
    }

    return topController!
}

3

这个解决方案是最完整的。它需要考虑以下因素: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
}

3

在雨燕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()
  }
}

用法:

let viewController = UIApplication.shared.topmostViewController()

2

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
}

1

我认为大多数答案都被完全忽略了UINavigationViewController,因此我通过以下实现处理了该用例。

+ (UIViewController *)topMostController {
    UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController || [topController isMemberOfClass:[UINavigationController class]]) {
        if([topController isMemberOfClass:[UINavigationController class]]) {
            topController = [topController childViewControllers].lastObject;
        } else {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}

1

我知道它很晚了,可能是多余的。但是以下是我想出的代码片段,它对我有用:

    static func topViewController() -> UIViewController? {
        return topViewController(vc: UIApplication.shared.keyWindow?.rootViewController)
    }

    private static func topViewController(vc:UIViewController?) -> UIViewController? {
        if let rootVC = vc {
            guard let presentedVC = rootVC.presentedViewController else {
                return rootVC
            }
            if let presentedNavVC = presentedVC as? UINavigationController {
                let lastVC = presentedNavVC.viewControllers.last
                return topViewController(vc: lastVC)
            }
            return topViewController(vc: presentedVC)
        }
        return nil
    }

0

这对于从任何根视图控件中找到顶部viewController 1都非常有用

+ (UIViewController *)topViewControllerFor:(UIViewController *)viewController
{
    if(!viewController.presentedViewController)
        return viewController;
    return [MF5AppDelegate topViewControllerFor:viewController.presentedViewController];
}

/* View Controller for Visible View */

AppDelegate *app = [UIApplication sharedApplication].delegate;
UIViewController *visibleViewController = [AppDelegate topViewControllerFor:app.window.rootViewController]; 

0

不知道这是否会通过找到最顶部的视图控制器来帮助您完成什么,但是我试图提供一个新的视图控制器,但是如果我的根视图控制器已经有一个模态对话框,它将被阻止,所以我将使用以下代码循环到所有模态视图控制器的顶部:

UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}

0

您可以通过使用找到最顶级的视图控制器

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];

除此之外,如果您实际阅读过该问题,self则没有navigationController属性。
Hot Licks

0

另一种解决方案依赖于响应者链,根据第一响应者的不同,响应链可能有效也可能无效:

  1. 获取第一响应者
  2. 获取与该第一响应者关联的UIViewController

伪代码示例:

+ (UIViewController *)currentViewController {
    UIView *firstResponder = [self firstResponder]; // from the first link above, but not guaranteed to return a UIView, so this should be handled more appropriately.
    UIViewController *viewController = [firstResponder viewController]; // from the second link above
    return viewController;
}

0

迅速:

extension UIWindow {

func visibleViewController() -> UIViewController? {
    if let rootViewController: UIViewController  = self.rootViewController {
        return UIWindow.getVisibleViewControllerFrom(rootViewController)
    }
    return nil
}

class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
if vc.isKindOfClass(UINavigationController.self) {

    let navigationController = vc as UINavigationController
    return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)

} else if vc.isKindOfClass(UITabBarController.self) {

    let tabBarController = vc as UITabBarController
    return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!)

} else {

    if let presentedViewController = vc.presentedViewController {

        return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

    } else {

        return vc;
    }
}
}

用法:

 if let topController = window.visibleViewController() {
            println(topController)
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.