博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
获取当前屏幕显示的视图控制器
阅读量:4946 次
发布时间:2019-06-11

本文共 6198 字,大约阅读时间需要 20 分钟。

  有个需求,就是在无论任何环境下,没有任何参数,我都要获得当前屏幕所显示的视图控制器。开始尝试。

一.

  有问题问Google,摸索阶段得到了这样的一段代码,时间大约是2016年五月:

1 //获取当前屏幕显示的viewcontroller   2 - (UIViewController *)getCurrentVC  { 3    4     UIViewController *result = nil;   5        6     UIWindow * window = [[UIApplication sharedApplication] keyWindow];   7     if (window.windowLevel != UIWindowLevelNormal)  {   8  9         NSArray *windows = [[UIApplication sharedApplication] windows];  10         for(UIWindow * tmpWin in windows)  {11   12             if (tmpWin.windowLevel == UIWindowLevelNormal)  {13   14                 window = tmpWin;  15                 break;  16             }  17         }  18     }  19       20     UIView *frontView = [[window subviews] objectAtIndex:0];  21     id nextResponder = [frontView nextResponder];  22       23     if ([nextResponder isKindOfClass:[UIViewController class]])  24         result = nextResponder;  25     else  26         result = window.rootViewController;  27       28     return result;  29 }

  这段代码由两个部分组成。我们项目是Swift语言为主体,我就尝试用Swift重写一下这个部分的代码:

  1.获得当前显示的window:

1 // 找到当前显示的window 2     class func getCurrentWindow() -> UIWindow? { 3          4         // 找到当前显示的UIWindow 5         var window: UIWindow? = UIApplication.shared.keyWindow 6         /**  7          window有一个属性:windowLevel 8          当 windowLevel == UIWindowLevelNormal 的时候,表示这个window是当前屏幕正在显示的window 9          */10         if window?.windowLevel != UIWindowLevelNormal {11             12             for tempWindow in UIApplication.shared.windows {13                 14                 if tempWindow.windowLevel == UIWindowLevelNormal {15                     16                     window = tempWindow17                     break18                 }19             }20         }21         22         return window23     }

  注释写的比较清楚了。如果你们的项目只有一个window,那么可以直接获得keyWindow就可以了;但是我建议还是判断一下,保险。

  2.获得当前的视图控制器:

1 // MARK: 获取当前屏幕显示的viewController 2     class func getCurrentViewController1() -> UIViewController? { 3          4         // 1.声明UIViewController类型的指针 5         var viewController: UIViewController? 6          7         // 2.找到当前显示的UIWindow 8         let window: UIWindow? = self.getCurrentWindow() 9         10         // 3.获得当前显示的UIWindow展示在最上面的view11         let frontView = window?.subviews.first12         13         // 4.找到这个view的nextResponder14         let nextResponder = frontView?.next15 16         if nextResponder?.isKind(of: UIViewController.classForCoder()) == true {17 18             viewController = nextResponder as? UIViewController19         }20         else {21             22             viewController = window?.rootViewController23         }24         25         return viewController26     }

  这两个方法一起使用。

  我先说结论:不好用。始终返回的都是 window的rootViewController。问题出在哪里?经过我的一路艰苦的判断,最终确定问题出在 let frontView = window?.subviews.first 这一句上。

  我不多说,我打印了下 window?.subviews 这个数组以及 nextResponder 这个UIResponder。结果如下:

Optional([
>,
>])Optional(
; layer =
>)

  可以看出,不管是subviews中的第一个还是第二个UIView,都是 UITransitionView 这样一个类型。这个类型是window与实际显示的view之间的过渡层,它的 nextResponder 直接就是window了。也就是说,这个view根本就不是当前显示在屏幕最上面的view。这条线索随之中断。

二.

  又继续查询,发现大家的博文写的都差不多,几乎都是上面最初的那段代码,一模一样。不过我终于还是找到这样一条博文:,与我遇到了几乎一样的问题;而他的解决方式就是沿着响应链继续向上寻找。而在评论区有更好的一个思路:对UIView创建分类,你给我一个view,我就还你控制这个view的第一个视图控制器:。

  我直接粘贴代码了:

1 extension UIView { 2      3     func findController() -> UIViewController! { 4      5         return self.findControllerWithClass(clzz: UIViewController.self) 6     } 7      8     func findNavigator() -> UINavigationController! { 9         10         return self.findControllerWithClass(clzz: UINavigationController.self)11     }12     13     func findControllerWithClass
(clzz: AnyClass) -> T? {14 15 var responder = self.next16 17 while(responder != nil) {18 19 if (responder?.isKind(of: clzz))! {20 21 return responder as? T22 }23 responder = responder?.next24 }25 26 return nil27 }28 }

  这个很明显,我们只需要拿到frontView之后,这样沿着响应链循环查找一下,应该就有结果了:

1 viewController = frontView?.findController()

  非常抱歉,又失败了。返回值为空。

  为此,我专门打印了一下在while循环中所有的responder的值:

1 Optional(
; layer =
>)2 Optional(
)3 Optional(
)

  恩,三步就出了App了。也就是说,这条响应链中根本就找不到UIViewController的身影。

  应该说,这个分类的思路是正确的,但是这个方法需要一个参数,就是一个view;它本质上是找到持有这个view的视图控制器;但是开头就说了,我们没有这样的一个view,没有任何参数。所以分类很好,但不适用我们这个问题。

三.

  好吧,看来通过window.subviews这个方法,似乎是行不通的。那么真的没有办法获得视图控制器了吗?

  其实办法还是有的。

  我们知道,一般我们的架构都是这样的:

  UIApplication -> UIWindow -> UITabBarController -> UINavigationController -> push/present出来的视图控制器

  我们要拿到的也就是第五层。什么办法呢?

  自然要请出我们的递归算法咯~

1 /* 递归找最上面的viewController */ 2     @objc class func topViewController() -> UIViewController? { 3          4         return self.topViewControllerWithRootViewController(viewController: self.getCurrentWindow()?.rootViewController) 5     } 6      7     @objc class func topViewControllerWithRootViewController(viewController :UIViewController?) -> UIViewController? { 8          9         if viewController == nil {10             11             return nil12         }13         14         if viewController?.presentedViewController != nil {15             16             return self.topViewControllerWithRootViewController(viewController: viewController?.presentedViewController!)17         }18         else if viewController?.isKind(of: UITabBarController.self) == true {19             20             return self.topViewControllerWithRootViewController(viewController: (viewController as! UITabBarController).selectedViewController)21         }22         else if viewController?.isKind(of: UINavigationController.self) == true {23             24             return self.topViewControllerWithRootViewController(viewController: (viewController as! UINavigationController).visibleViewController)25         }26         else {27             28             return viewController29         }30     }

  从头上一路向下找,最终找到当前显示的控制器。简单粗暴,但其实处处是美感,这正是递归的魅力啊。

  最终调用 topViewController() 即可。

转载于:https://www.cnblogs.com/SoulKai/p/6278373.html

你可能感兴趣的文章
介绍一个很有意思的脚本语言:GolfScript
查看>>
使用Dapper查询记录是否存在
查看>>
Linux的基本命令
查看>>
Request,Request.QueryString,Request.Form
查看>>
phonegap file api
查看>>
转摘—Java中用单例模式的好处
查看>>
机器学习2(降维)
查看>>
【NOIP2012】【Luogu1075】质因数分解(模拟)
查看>>
02号团队-团队任务3:每日立会(2018-12-05)
查看>>
sql 语法大全
查看>>
SQLite移植手记1
查看>>
Java AmericanFlagSort
查看>>
Mysql远程连接报错
查看>>
C# windows程序应用与JavaScript 程序交互实现例子
查看>>
sqlServer去除字段中的中文
查看>>
HashMap详解
查看>>
Adobe Scout 入门
查看>>
51nod 1247可能的路径
查看>>
js05-DOM对象二
查看>>
mariadb BINLOG_FORMAT = STATEMENT 异常
查看>>