[ZT]UIView的剖析!
http://blog.csdn.net/mengtnt/article/details/6716289
前面说过UIViewController,但是UIView也是在MVC中非常重要的一层?。正是因为UIView是Iphone下所有界面的基础,所以官方专门写了一个文档“View?Programming?Guide?for?iOS”。通过这个可以很好的了解UIView的功能。
??????? 先来看看官方API的解释:The?UIView?class?defines?a?rectangular?area?on?the?screen?
and?the?interfaces?for?managing?the?content?in?that?area.
?At?runtime,?a?view?object?handles?the?rendering?of?any?content?in?its?area
?and?also?handles?any?interactions?with?that?content.(UIView在屏幕上定义了一个矩形区域和管理区域内容的接口。在运行时,一个视图对象控制该区域的渲染,同时也控制内容的交互。)。所以说UIView具有三个基本的功能,画图和动画,管理内容的布局,控制事件。正是因为UIView具有这些功能,它才能担当起MVC中视图层的作用。
???????? UIView咋看起来很复杂,官方API中各种函数接口,要学过运用庖丁解牛的思想,逐个分析,因为再复杂的东西都是有简单的东西构成的。回到刚才提到的UIView的三个基本功能就可以容易的分离出UIView不同的功能是怎么组合起来的。首先看视图最基本的功能显示和动画,其实UIView的所有的绘图和动画的接口,都是可以用CALayer和CAAnimation实现的,也就是说苹果公司是不是把CoreAnimation的功能封装到了UIView中,这个文档中没有提到过,也没法断言。但是每一个UIView都会包含一个CALayer,并且CALayer里面可以加入各种动画。再次我们来看UIView管理布局的思想其实和CALayer也是非常的接近的。最后控制事件的功能,是因为UIView继承了UIResponder。经过上面的分析很容易就可以分解出UIView的本质。UIView就相当于一块白墙,这块白墙只是负责把加入到里面的东西显示出来而已。
????????????????
?????????????????????????????????????????????????????????????????????????????????????????????????????????????? 图2???????????????????????????????????????????????????????????
???????? 虽然CALayer跟UIView十分相似,也可以通过分析CALayer的特点理解UIView的特性,但是毕竟苹果公司不是用CALayer来代替UIView的,否则苹果公司也不回设计一个UIView类了。就像官方文档解释的一样,CAlayer层树是cocoa视图继承树的同等物,它具备UIView的很多共同点,但是Core?Animation没有提供一个?方法展示在窗口。他们必须宿主到UIView中,并且UIView给他们提供响应的方法。所以UIReponder就是UIView的又一个大的特性。
2.UIView继承的UIResponder
?????? UIResponder是所有事件响应的基石,官方也提供了一个重要的文档给开发者参考”Event?Handling?Guide?for?iOS”。
????? ?事件(UIEvent)是发给应用程序,告知用户的行动的。在IOS中事件有三种事件:多点触摸事件,行动事件,远程控制事件。三种事件定义如下:
?????????????????????????????????????????????????????? 图3
首先是被点击的该视图响应时间处理函数,如果没有响应函数就逐级的向上面传递,直到有响应处理函数,或者该消息被抛弃。至于苹果公司是如何让事件消息这样流动的,在下面的分析中,可以了解一些,至于深层的原理还的进一步挖掘。
??????? 这里重点看三个事件?中的多点触摸事件,也就是UITouch事件,下图是UIEvent中封装的UITouch内容
?
????????????????????????????????????????????????????????????????????????????????????????? 图4
????????? 关于UIView的触摸响应事件中,这里有一个常常容易迷惑的方法hitTest:WithEvent。先来看官方的解释:This?method?traverses?the?view?hierarchy?by?sending?the?pointInside:withEvent:?message?
to?each?subview?to?determine which?subview?should?receive?a?touch?event.?
If?pointInside:withEvent:?returns?YES,?then?the?subview’s?hierarchy?is?traversed;
?otherwise,?its?branch?of?the?view?hierarchy?is?ignored.
?You?rarely?need?to?call?this?method?yourself,?
but?you?might?override?it?to?hide?touch?events?from?subviews.(通过发送PointInside:withEvent:消息给每一个子视图,这个方法遍历视图层树,来决定那个视图应该响应此事件。如果PointInside:withEvent:返回YES,然后子视图的继承树就会被遍历;否则,视图的继承树就会被忽略。你很少需要调用这个方法,仅仅需要重载这个方法去隐藏子视图的事件)。从官方的API上的解释,可以看出?hitTest方法中,要先调用PointInside:withEvent:,看是否要遍历子视图。如果我们不想让某个视图响应事件,只需要重载PointInside:withEvent:方法,让此方法返回NO就行了。不过从这里,还是不能了解到hitTest:WithEvent的方法的用途。
???????? 下面再从”Event?Handling?Guide?for?iOS”找答案,Your?custom?responder?can?use?hit-testing?to?find?the?subview?or?sublayer?of?itself?that?is?"under”?a?touch,?and?then?handle?the?event?appropriately。从中可以看出hitTest主要用途是用来寻找那个视图是被触摸了。看到这里对hitTest的调用过程还是一知半解。我们可以实际建立一个工程进行调试。建立一个MyView里面重载hitTest和pointInside方法:
}????????? 所以区别这两个手势的思想,就是判断tapcount如果发现touchEnd的时候tapcount是2就取消第一次执行的动作。但是这一点是否想过,苹果公司是如何判断tapcount的,比如说我在屏幕上按了下去,过了一分钟后松开,那么在touchEnd方法中捕捉到的touch事件和我点击一下屏幕就起来一样么?答案是不一样的,可以写程序亲自试验以下,按下去一分钟再松开,这里没必要一分钟了,就几秒也足够了,你会发现再touchEnd中tapCount为0,而点击一下松开的tapCount为1。还有一种情况就是双击,如果我双击间隔的时间超过大概4,5秒钟,再次侦测touchEnd中的tapCount就会发现是1,而正常的双击tapCount为2。这里和hitTest执行三次,并且前两次记录的时间是上一次触摸手势的时间,后一次才是本次触摸手势的时间,有没有关系,官方没有任何解释,这里也只能臆测。是不是用来区分上面所说的情况,也就是说根据这个事件timestamp来改变UITouch中tapCount的次数,还希望那位高手给予解释。所以上面提到的UIEvent,这个事件为何能向苹果官方解释的那样流动,这里也就可见一斑了。