获得最顶级的UIViewController

Zoy*_*oyt 174 uiviewcontroller swift

如果UIViewController没有访问权限,我似乎无法获得最高分UINavigationController.这是我到目前为止:

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)
Run Code Online (Sandbox Code Playgroud)

但是,它似乎没有做任何事情.在keyWindowrootViewController似乎是非零值太多,所以可选的链接不应该是一个问题.

注意: 做这样的事情是个坏主意.它打破了MVC模式.

DLe*_*nde 261

有这个扩展

斯威夫特2.*

extension UIApplication {
    class func topViewController(controller: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(selected)
            }
        }
        if let presented = controller?.presentedViewController {
            return topViewController(presented)
        }
        return controller
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

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)

您可以在控制器上的任何位置使用它

if let topController = UIApplication.topViewController() {

}
Run Code Online (Sandbox Code Playgroud)

  • 我试图对这个答案做一个重要的编辑,但它被拒绝了(我不明白为什么并且给出的模板原因没有意义):在递归中使用它之前检查nav.visibleViewController是否为零是很重要的调用(就像tab.selectedViewController一样被检查),否则,如果它是nil,你将进入一个递归的无限循环. (4认同)
  • 我认为将它作为UIViewController的静态函数更合乎逻辑 (3认同)

ric*_*rbh 249

presentViewController显示一个视图控制器.它不会返回视图控制器.如果您没有使用a UINavigationController,那么您可能正在寻找presentedViewController并且您需要从根开始并遍历所呈现的视图.

if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}
Run Code Online (Sandbox Code Playgroud)

对于Swift 3+:

if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}
Run Code Online (Sandbox Code Playgroud)

  • @ProfessorTom只要`topController.presentedViewController`返回一些东西(即控制器有一个呈现的子控制器),循环就会继续.它是``让'强制执行`topController.presentedViewController`必须返回的事实.如果它返回nil(即,这是控制器没有呈现的子节点),那么它将停止循环.在循环体中,它将子节点重新指定为当前的"topController",并再次循环,沿着视图控制器层次结构向下循环.它可以重新分配`topController`,因为它是外部`if`语句中的`var`. (12认同)
  • 有人可以解释while循环吗?对我来说,似乎没有什么可以循环的;我什至不确定为什么会编译。 (2认同)
  • 我记得在 WPF 时代,我必须循环遍历视觉树来做类似的事情,所以它并不像人们想象的那么脏。此外,我比其他答案更喜欢这个答案,因为它使用循环而不是递归函数,因为递归函数可能需要深度调用堆栈、更多内存,并且效率较低。感谢您确认这就是方法。 (2认同)

Har*_*kar 50

对于swift 4.2来找到最顶层的viewController

// MARK: UIApplication extensions

extension UIApplication {

    class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return getTopViewController(base: nav.visibleViewController)

        } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
            return getTopViewController(base: selected)

        } else if let presented = base?.presentedViewController {
            return getTopViewController(base: presented)
        }
        return base
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用

if let topVC = UIApplication.getTopViewController() {
   topVC.view.addSubview(forgotPwdView)
}
Run Code Online (Sandbox Code Playgroud)

  • “keyWindow”在 iOS 13.0 中已弃用 /sf/answers/4052930941/ (4认同)
  • 绝妙的解决方案。谢谢! (2认同)
  • iOS 13.0 中已弃用“keyWindow”。 (2认同)

atu*_*tri 18

我使用以下代码来获取 topViewController-

它向后兼容旧的 iOS 版本并且也照顾 UIScene

extension UIApplication {
    func topViewController() -> UIViewController? {
        var topViewController: UIViewController? = nil
        if #available(iOS 13, *) {
            for scene in connectedScenes {
                if let windowScene = scene as? UIWindowScene {
                    for window in windowScene.windows {
                        if window.isKeyWindow {
                            topViewController = window.rootViewController
                        }
                    }
                }
            }
        } else {
            topViewController = keyWindow?.rootViewController
        }
        while true {
            if let presented = topViewController?.presentedViewController {
                topViewController = presented
            } else if let navController = topViewController as? UINavigationController {
                topViewController = navController.topViewController
            } else if let tabBarController = topViewController as? UITabBarController {
                topViewController = tabBarController.selectedViewController
            } else {
                // Handle any other third party container in `else if` if required
                break
            }
        }
        return topViewController
    }
}
Run Code Online (Sandbox Code Playgroud)

可以这样使用:

let topController = UIApplication.shared.topViewController()
topController?.present(controllerToPresent, animated: true, completion: nil)
Run Code Online (Sandbox Code Playgroud)


Bob*_*j-C 16

extension UIWindow {

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

    static func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
        if let navigationController = vc as? UINavigationController,
            let visibleController = navigationController.visibleViewController  {
            return UIWindow.getVisibleViewControllerFrom( vc: visibleController )
        } else if let tabBarController = vc as? UITabBarController,
            let selectedTabController = tabBarController.selectedViewController {
            return UIWindow.getVisibleViewControllerFrom(vc: selectedTabController )
        } else {
            if let presentedViewController = vc.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)
            } else {
                return vc
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

if let topController = window.visibleViewController() {
    println(topController)
}
Run Code Online (Sandbox Code Playgroud)


小智 8

import UIKit

extension UIApplication {

    // MARK: Choose keyWindow as per your choice
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }

    // MARK: Choose keyWindow as per your choice
    var keyWindow: UIWindow? {
        UIApplication.shared.windows.first { $0.isKeyWindow }
    }

    class func topMostViewController(base: UIViewController? = UIApplication.shared.currentWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return topMostViewController(base: nav.visibleViewController)
        }

        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController, top.view.window != nil {
                return topMostViewController(base: top)
            } else if let selected = tab.selectedViewController {
                return topMostViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topMostViewController(base: presented)
        }
        return base
    }
}
Run Code Online (Sandbox Code Playgroud)


Tib*_*abo 6

基于Dianz的答案,Objective-C版本

- (UIViewController *) topViewController {
   UIViewController *baseVC = UIApplication.sharedApplication.keyWindow.rootViewController;
   if ([baseVC isKindOfClass:[UINavigationController class]]) {
       return ((UINavigationController *)baseVC).visibleViewController;
   }

   if ([baseVC isKindOfClass:[UITabBarController class]]) {
       UIViewController *selectedTVC = ((UITabBarController*)baseVC).selectedViewController;
       if (selectedTVC) {
           return selectedTVC;
       }
   }

   if (baseVC.presentedViewController) {
       return baseVC.presentedViewController;
   }
   return baseVC;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 我对本网站的答案和评论做了一些测试.对我来说,以下工作

extension UIViewController {
    func topMostViewController() -> UIViewController {

        if let presented = self.presentedViewController {
            return presented.topMostViewController()
        }

        if let navigation = self as? UINavigationController {
            return navigation.visibleViewController?.topMostViewController() ?? navigation
        }

        if let tab = self as? UITabBarController {
            return tab.selectedViewController?.topMostViewController() ?? tab
    }

        return self
    }
}

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

然后,通过以下方式获取顶部viewController:

UIApplication.shared.topMostViewController()
Run Code Online (Sandbox Code Playgroud)


小智 6

iOS13+ //顶部最视图控制器

extension UIViewController {
    func topMostViewController() -> UIViewController {
        if self.presentedViewController == nil {
            return self
        }
        if let navigation = self.presentedViewController as? UINavigationController {
            return navigation.visibleViewController!.topMostViewController()
        }
        if let tab = self.presentedViewController as? UITabBarController {
            if let selectedTab = tab.selectedViewController {
                return selectedTab.topMostViewController()
            }
            return tab.topMostViewController()
        }
        return self.presentedViewController!.topMostViewController()
    }
}

extension UIApplication {
    func topMostViewController() -> UIViewController? {
        return UIWindow.key!.rootViewController?.topMostViewController()
    }
}

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}

//use let vc = UIApplication.shared.topMostViewController()

// End top Most view Controller
Run Code Online (Sandbox Code Playgroud)


Pon*_*y47 5

我喜欢@ dianz的答案,所以这里是它的快速3版本.它基本上是相同的,但他缺少大括号,一些语法/变量/方法名称已经改变.所以在这里!

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

用法仍然完全相同:

if let topController = UIApplication.topViewController() {
    print("The view controller you're looking at is: \(topController)")
}
Run Code Online (Sandbox Code Playgroud)


小智 5

使用此代码查找最顶级的UIViewController

func getTopViewController() -> UIViewController? {
    var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
    while topController?.presentedViewController != nil {
        topController = topController?.presentedViewController
    }
    return topController
}
Run Code Online (Sandbox Code Playgroud)

  • 这与rickerbh的答案有何不同? (2认同)

Rya*_*ner 5

@AlberZou使用计算变量而不是函数的轻微变化

extension UIViewController {
  var topMostViewController : UIViewController {

    if let presented = self.presentedViewController {
      return presented.topMostViewController
    }

    if let navigation = self as? UINavigationController {
      return navigation.visibleViewController?.topMostViewController ?? navigation
    }

    if let tab = self as? UITabBarController {
      return tab.selectedViewController?.topMostViewController ?? tab
    }

    return self
  }
}

extension UIApplication {
  var topMostViewController : UIViewController? {
    return self.keyWindow?.rootViewController?.topMostViewController
  }
}
Run Code Online (Sandbox Code Playgroud)

然后说

if let topViewControler = UIApplication.shared.topMostViewController {
    ... do stuff
}
Run Code Online (Sandbox Code Playgroud)


Muh*_*qas 5

适用于Swift 5+iOS 13+

extension UIViewController {
    static func topMostViewController() -> UIViewController? {
        if #available(iOS 13.0, *) {
            let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            return keyWindow?.rootViewController?.topMostViewController()
        }
        
        return UIApplication.shared.keyWindow?.rootViewController?.topMostViewController()
    }
    
    func topMostViewController() -> UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.topMostViewController()
        }
        else if let tabBarController = self as? UITabBarController {
            if let selectedViewController = tabBarController.selectedViewController {
                return selectedViewController.topMostViewController()
            }
            return tabBarController.topMostViewController()
        }
            
        else if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        
        else {
            return self
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

当您在没有 UIViewController 实例的情况下获得 topMostViewController 时

guard let viewController = UIViewController.topMostViewController() else { return }
print(viewController)
Run Code Online (Sandbox Code Playgroud)

当您获得 UIViewController 实例的 topMostViewController 时

let yourVC = UIViewController()
guard let vc = yourVC.topMostViewController() else { return }
print(vc)
Run Code Online (Sandbox Code Playgroud)


moh*_*sen 5

SWIFT 5.2中

您可以使用下面的代码:

import UIKit

extension UIWindow {
    static func getTopViewController() -> UIViewController? {
        if #available(iOS 13, *){
            let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            
            if var topController = keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        } else {
            if var topController = UIApplication.shared.keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        }
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)