在iOS Swift中获取顶层ViewController

7
我希望实现一个单独的ErrorHandler类,它能够在某些事件发生时显示错误信息。这个类的行为应该从其他不同的类中调用。 当发生错误时,将有一个UIAlertView作为输出。AlertView的显示应该始终在最上层。所以无论Error从哪里抛出,最上面的viewController都应该显示AlertMessage(例如,当异步后台进程失败时,我想要一个错误消息,无论前景中显示了什么视图)。
我已经找到了几个看起来可以解决我的问题的gist(见下面的代码)。但调用UIApplication.sharedApplication().keyWindow?.visibleViewController()返回一个nil值。
来自gist的扩展
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;
  }
}
}
}

请问您能否尽可能地分享您的ErrorHandler类在GitHub或其他地方?非常感谢! - mfaani
2个回答

25

Amit89提出了一种解决方案。您需要调用AppDelegate的.window属性。 因此,我将下面链接中的Swift代码更改为预期工作以查找最顶层的视图控制器。请确保视图已经存在于视图层次结构中。因此,此方法不能从.viewDidLoad中调用。

扩展以查找最顶层的视图控制器*

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)
    }
    return base
  }
}

这段代码源自GitHub用户Yonat在一个objectiveC等效的评论中创建。我只更改了一些代码片段,使其在没有.keyWindow属性的情况下正常工作。


10
我正在使用你们的扩展程序来查找最上层的ViewController,但是如果弹出警告框(alert),上面的代码会返回UIAlertController。我该如何在UIAlertController下获得最上层的视图控制器? - Luda

1

你从相同的链接尝试过这个吗?

let appDelegate = UIApplication.sharedApplication().delegate as! MYAppDelegate//Your app delegate class name.


extension UIApplication {
    class func topViewController(base: UIViewController? = 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)
        }
        return base
    }
}

是的,我做了: 致命错误:在解包一个可选值时意外地发现nil 问题在于 'UIApplication.sharedApplication().keyWindow?.rootViewController)' 返回了 nil。 - gutenmorgenuhu
不要使用关键窗口,而是使用应用程序委托类的窗口属性。 - Amit89
如果它有帮助,应该被接受为正确答案。 - Amit89

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