UIAlertController下的最顶层ViewController

6
我正在使用以下扩展程序来查找最顶层的ViewController。 如果有弹窗出现,上面的代码将会给出UIAlertController。 如何获取在UIAlertController下方的顶部视图控制器?

优秀的答案可以在此处找到:https://dev59.com/iF8d5IYBdhLWcg3wsDxW - kelin
@Luda,你解决了这个问题吗?能否请提供一下代码。我也遇到了同样的问题。谢谢! - Steve Gear
@SteveGear 很抱歉,我不记得了。请查看下面的答案。 - Luda
6个回答

5
创建一个像下面这样的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
    }

}

1
您可以检查下一个视图控制器是否为 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 为空 :( - Luda
你确定把它放在 if let presented = base?.presentedViewController { 上面了吗? - Jelly
我尝试了一下,调试器没有进入这个if语句。但是当我加上了“if let presented = base as? UIAlertController {”后,它就进入了。这意味着UIAlertController是基类。 - Luda
base!.presentedViewController 的祖先是 base - Jelly
我得到了 base = UIApplicationRotationFollowingController 的错误。 - Luda
显示剩余3条评论

1

您可以使用 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上进行测试。


1
它造成了无限递归。 - Luda
我们只需要找出谁是UIAlertController的祖先。 - Luda
抱歉,应该是 UIAlertController 而不是 UIAlertViewController。已更新我的回答。 - atulkhatri
无限递归仍然发生。 - Brian Ogden
我没有使用任何第三方控制器。 - Brian Ogden
显示剩余2条评论

0

我认为你想在当前最上面可见的ViewController上推送一个新的VC,而这个VC是UIAlertController,然后这个UIAlertController会立即消失,因为被推送的新VC也被关闭了。最终,你无法推送一个新的VC。

问题在于,如果你新建一个UIAlertView,然后调用show方法,Cocoa Touch会初始化一个新的窗口,其rootViewController是UIApplicationRotationFollowingController,其presentingViewController是UIAlertController。因此,你无法遍历UIAlertController下面的最高层VC,因为它存在于另一个窗口中!

因此,如果从keyWindow?.rootViewController遍历topViewController,找到了一个UIAlertController,则再次调用topViewController,但是从你想要的窗口遍历,例如:(UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController


0
我使用了这个扩展来获取 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
}

}


0

这是正确的:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接