UIAlertController 下最顶层的 ViewController

Posted

技术标签:

【中文标题】UIAlertController 下最顶层的 ViewController【英文标题】:Top most ViewController under UIAlertController 【发布时间】:2016-03-29 12:36:18 【问题描述】:

我正在使用以下扩展程序来查找top most ViewController。 如果出现警报,上面的代码会给出UIAlertController。 如何获得顶视图控制器 UIAlertController

【问题讨论】:

可以在这里找到好的答案:***.com/q/26554894/3050403 @Luda 你解决了这个问题吗.. 你能提供代码吗?我也面临同样的问题..谢谢! @SteveGear 不幸的是我不记得了。请检查下面的答案 【参考方案1】:

创建一个如下所示的 UIApplication 扩展,UIApplication.topViewController() 将返回UIAlertController 下最顶部的UIViewController

ios 13+

extension UIApplication 

    class func topViewController(controller: UIViewController? = UIApplication.shared.windows.first?.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)
        
        if let alert = controller as? UIAlertController 
            if let navigationController = alert.presentingViewController as? UINavigationController 
                return navigationController.viewControllers.last
            
            return alert.presentingViewController
        
        return controller
    


iOS 12-

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)
        
        if let alert = controller as? UIAlertController 
            if let navigationController = alert.presentingViewController as? UINavigationController 
                return navigationController.viewControllers.last
            
            return alert.presentingViewController
        
        return controller
    


【讨论】:

【参考方案2】:

您可以使用presentingViewController 属性获取UIAlertController 的父控制器

extension UIApplication 
  class func topViewController(base: UIViewController? = (UIApplication.sharedApplication().delegate as! AppDelegate).window?.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 alert = base as? UIAlertController 
      if let presenting = alert.presentingViewController 
        return topViewController(base: presenting)
      
    
    if let presented = base?.presentedViewController 
      return topViewController(base: presented)
    
    return base
  

在您的代码中使用这些更改,未在 XCode 上测试。

【讨论】:

无限递归 我们只需要找出 UIAlertController 的祖先是谁 对不起,它是 UIAlertController 而不是 UIAlertViewController。更新了我的答案。 无限递归仍然发生 @BrianOgden 您能否确认您是否没有使用任何第三方控制器作为您的window.rootViewController 像任何侧边菜单之类的东西?【参考方案3】:

我使用这个扩展来获取 UIAlertController 下最顶层的视图控制器,基本上我所做的就是在找到一个 UIAlertController 时停止寻找顶层视图控制器。

extension UIApplication 

var topViewController: UIViewController? 
    var viewController = keyWindow?.rootViewController
    guard viewController != nil else  return nil 
    var presentedViewController = viewController?.presentedViewController
    while presentedViewController != nil, !(presentedViewController is UIAlertController) 
        switch presentedViewController 
        case let navagationController as UINavigationController:
            viewController = navagationController.viewControllers.last
        case let tabBarController as UITabBarController:
            viewController = tabBarController.selectedViewController
        default:
            viewController = viewController?.presentedViewController
        
        presentedViewController = viewController?.presentedViewController
    
    return viewController

【讨论】:

【参考方案4】:

您可以检查下一个 viewController 是否为UIAlertController,如果是则返回其父级。像这样的:

if let presented = base as? UIAlertController 
  return base.presentingViewController

在返回之前将其添加到您使用的扩展中。

更新

extension UIApplication 
   class func topViewController(base: UIViewController? =    (UIApplication.sharedApplication().delegate as! AppDelegate).window?.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)
      

      if let alert = base as? UIAlertController 
         return alert.presentingViewController
      

      return base
   

【讨论】:

base.parentViewController 为 nil :( 不...基础是 UIAlertController。不是基础?.presentedViewController 你确定把它放在if let presented = base?.presentedViewController 上面吗? 我做了,如果调试器没有输入这个。但是当我添加 if let present = base as? UIAlertController 它确实进入了。这意味着 UIAlertController 是基础 base 不是 UIAlertController。但是 base!.presentedViewController 是我们只需要找出谁是 UIAlertController 的祖先【参考方案5】:

我认为你想在当前顶部可见的 VC 上推送一个新的 VC,它是一个 UIAlertController,然后这个 UIAlertController 将立即消失,导致推送的新 VC 也被关闭。最后,你不能推送新的 VC。

问题是,如果你新建了一个 UIAlertView,然后调用show,Cocoa Touch 会初始化一个新窗口,rootViewController 是 UIApplicationRotationFollowingController,presentingViewController 是 UIAlertController。所以不能遍历 UIAlertController 下最顶层的 VC,因为它存在于另一个窗口中!

所以如果topViewControllerkeyWindow?.rootViewController遍历,找到UIAlertController,再次调用topViewController但是从window遍历你想要的,比如(UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController

【讨论】:

【参考方案6】:

这是正确的:

    func firstApplicableViewController() -> UIViewController? 
        if (self is UITabBarController) 
            let tabBarController = self as? UITabBarController
            return tabBarController?.selectedViewController?.firstApplicableViewController()
         else if (self is UINavigationController) 
            let navigationController = self as? UINavigationController
            return navigationController?.visibleViewController?.firstApplicableViewController()
         else if (self is UIAlertController) 
            let presentingViewController: UIViewController = self.presentingViewController!
            return presentingViewController.firstApplicableViewController()
         else if self.presentedViewController != nil 
            let presentedViewController: UIViewController = self.presentedViewController!
            if (presentedViewController is UIAlertController) 
                return self
             else 
                return presentedViewController.firstApplicableViewController()
            
         else 
            return self
        
    

【讨论】:

以上是关于UIAlertController 下最顶层的 ViewController的主要内容,如果未能解决你的问题,请参考以下文章

带有呈现的 UIAlertController 的 UINavigationController 被另外解雇

不在视图控制器中时如何呈现 UIAlertController?

iOS开发 - 获取当前View最顶层的ViewController

iOS开发 - 获取当前View最顶层的ViewController

如何对xilinx FPGA进行bit文件加密

verilog学习实战1之基础练习