浅谈iOS调试技巧的实际应用(二) UI篇

来源:互联网 发布:2008数据库重建日志 编辑:程序博客网 时间:2024/05/29 14:59

做iOS开发, 大部分的工作跟UI相关,而分析UI最重要的就是分析UI的Hierarchy,这篇就是围绕View Hierarchy来详细介绍UI相关的调试技巧。

  • 获取Hierarchy

  巧妇难为无米之炊,首先我们得得到Hierarchy,方法是调用某个UIView对象或其子类的recursiveDescription方法。

  recursiveDescription方法是UIView的私有方法。苹果未公开该方法。常用的的场合是, 在任意断点处执行

po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

po [oneView recursiveDescription]

  该方法将递归打印该View的所有子View的层次关系,效果如下图:

  

 

  • 通过Hierarchy能解决哪些问题? 可用在哪些场景?

 

    •   场景1: 获取当前正在显示的是哪个页面(ViewController)?

        好吧, 同事在很专心的思索某个问题, 我不忍心打扰他,我想知道他的VC是什么, 做了什么事情?或者想知道某个问题它是如何处理的。

        这时, 你先在模拟器里定位到他的页面, 然后pause program,再在控制台打印当前keyWindow的Hierarchy,然后从输出信息里, 从按照等级从高到低(将View前的“|”的数量定义为该View的等级,越少等级越高), 然后同一等级下从下往上找。因为,在打印信息里,在同一层级下,越往下的View,zOrder越高。zOrder高的View当然会盖住zOrder低的View。

        以上图为例, 你发现<UIView: 0xa1769c0...>就是你要找的View。因为前面的都是系统的View。它会将你包裹起来做其他事情。

        好了,View知道了, 那VC呢?

        对象与对象之间的关系在某种程度上可以分为3种:1.互不相知, 2.单向引用, 3,双向引用。后面2种有点像链表。我们都知道通过VC的view属性就可以找到View了。但是,好像从来没有过通过view找到它的VC的经验?对吧。但是,也许, 你会猜,View应该也会记住它的VC是谁吧。是的。 事实确实如此。

        

        如图(UIView的.h文件), 你发现你找到证据了,VC是view的一个代理,对的, 因为View有时候需要让VC去做一些事情。 但是,这只是结构体中的一个变量了。你不能通过给对象发送消息来获取这个值啊。因为没有这个方法。 那怎么办呢? 对了。 这时OC Runtime的KVO机制就可以发挥作用了。

[oneView ValueForKey:@"_viewDelegate"]

 

        上面这个方法就可以获取view的VC是谁了。

 

        除了上面KVO的方法?还有其他方法吗?答案是:有的。

        如果你了解UI的事件传递机制。你会知道消息会一直从hitTestView一直往上forward, 直到UIApplication为止。这个工作是由基类UIResponder完成的。当一个Responder对象不对事件处理时, 它会调用nextResponder方法找到它的下一个对象, 然后丢给它处理。

        好了。如果一个View有它的VC, 按照事件传递机制, 它的nextResponder必定是VC。对了,如此问题就解决了。 调用View的nextResponder就一定能找到它的VC是谁。是不是很简单呢?

[oneView nextResponder]

 

 

    •   场景2: 为什么我的视图没显示呢?或者在某个时候不见了呢?

        这个问题比较复杂。因为View的很多属性都会影响视图的显示。解决问题的方法如下:

         1. 自身原因

          frame:有可能origion映射到Window上时,已经超出Window的边界了。(可以通过UIView的convert方法获取)

              有可能你的width或者height中有一个为0.

          hidden: 有可能某个时候hidden属性被置为YES。

          alpha:有可能某个时候alpha属性被置为0.

         2. superView的原因

          也许你的view根本没add到superView上,或者某个时候被移除。也许是由于superView的上述“自身原因”,也许是superView的superView... 。

         

         幸好,frame属性在Hierarchy信息中被打出, 如果hidden属性为YES,alpha为0, Hierarchy会特意打印出来。所以, 基于Hierarchy你就可以分析某个试图为什么没有正确显示的原因。

 

    •   场景3: 为什么我的视图(通过UIControll或者加UIGesture)点下去没反应?

        这个问题很复杂,有可能它不是hitTestView, 有可能消息被UIGesture优先捕获了。这里牵涉到消息机制, 暂时不在这里说明。

        但是, 下面可以针对hitTestView这个原因来进行分析。

        如果它不被确定为hitTestView。 那么

        1.自身原因

          frame: 有可能你的width或者height为0(如果一个视图的clipsToBounds为NO时,有可能他的frame是空,但是依然能显示出来。例如UIImageView),此时hitTest方法不能判定该View能相应事件。

              有可能自身的frame超出superView的frame。 那么超出部分的事件将不会将hitTestView判定给该View。这个问题比较隐蔽。也会经常遇到。

          userInteractionEnabled: 为NO时,不能响应事件。

          Hidden:为YES时,不能响应事件。

        2.superView的原因

          superView的上述“自身原因”也会影响到子View响应事件。

        类似场景2. 上面的问题也可以通过查看打印的Hierarchy分析。

 

    基于Hierarchy的UI调试方法还有通过第三方工具, 例如Hierarchy Viewer可视化工具等。

 

    当然,通过Hierarchy还可以做更多的事情。 暂时先介绍到这里。请待下一次再进行更新。


0 0