我们所看到的程序
对于一切IOS APP来说,我们看的的内容,都是UIView所呈现的。
UIView如场景,UIWindow如舞台,UIView粉墨登场在UIWindow这个舞台上,使我们看到丰富多彩的界面UI。UIWindow本身没有任何内容,它只提供了一个场所来让这些UIView来显示,切换。
通常,一个APP仅有一个UIWindow作为显示的场所,当我们要进行多屏显示时,才会使用到多个UIWindow。
UIView的基本结构
1、UIView附着于UIWindow上,只有放在UIWindow上的View,才能被我们看到。
2、UIView本身又可作为容器显示并管理SubView,使界面更丰富。
3、每个UIView都有一个对应的Core Animation Layer类对象作支持。Layer类对象一般是CALayer类对象。该对象存储对应UIView对象的一些信息数据,并处理UIView的动画操作。
有了CALayer对象,主要会提供如下几点作用:
(1)CALayer对象存储了对应UIView对象的信息数据,这可以使UIView的渲染次数大大减少,我们可以尽可能的直接读取CALayer对象里面的存储的已经渲染过的数据,而不需要每次都要渲染UIView。
(2)正是因为CALayer对象存储了UIView的信息,才使得UIView的当前内容可以被操作,进而实现UIView的动画效果。
(3)我们可以通过直接操作CALayer对象来实现更丰富灵活的显示及动画效果。
UIWindow, UIView, Core Animation Layer对象关系如下图所示:
View的等级及SubView的管理
如前所述,UIView可以添加子View,进而形成superView,subView这样的父子逻辑关系。
subView被父View放到一个子View队列里面进行管理。
view显示的层级关系
最后加入的view会遮挡前面的view。
事件响应链(Event responder chain)
当App获取到用户的操作事件后,会首先将该event传递给发生该事件最上层的subview,若该subview不响应该事件,则会传递至该view的superview进行响应,这样一直传递下去,直到该事件被响应或由程序丢弃不处理为止。这就是所谓的Event responder chain。
View的内容绘制周期
在IOS中,UIView类采取一种按需策略来绘制View的显示内容。所谓按需策略,就是指仅当你明确告知系统需要对View进行内容重绘时,系统才会调用你的绘制函数重新绘制VIew的内容,否则,系统在大多数情况下仅使用View的内容快照图片来代替View的内容显示。
具体实现如下:
1、当一个View第一次显示在屏幕时,系统会自动调用View的绘制函数,完成内容显示,同时,为内容保留一份快照图片。
2、若View的内容不发生改变,则在大多数情况下,系统仅使用快照图片来表示View内容。这里注意,系统不会主动询问是否View的内容发生了改变,需要你主动通知系统内容发生改变从而更新View显示内容。
3、当你对View的内容做出了改变,调用
setNeedsDisplay
or setNeedsDisplayInRect:
方法通知系统内容发生改变,需要重绘View界面。
4、系统得知内容发生改变后,不会立即重绘View界面,而是当这轮run loop结束,准备重绘内容时,才会对改变内容的View进行重绘。
5、当系统对View进行重绘时,流程并不是统一的。对于自定义的View而言,我们需要重写
drawRect: 方法。
当然,也可以通过直接改变View对应的Layer对象的方式来改变View的内容。
6、当系统完成对View的重绘后,会采集一张新的内容快照来代表View的内容来用作多数时间的View显示。
注意,一般的View的几何形变(如拉伸缩小,不会引起内容重绘,而仅仅是内容快照的拉伸缩小)。
View的内容模式
如前所述,当View显示在屏幕上后,系统会用一张快照来代替表示内容。
这会照成这么一种情况,当我们更改View的frame等属性时,其内容(内容快照),并不一定会同时改变。对于内容显示的方式,取决于UIView对象的Content Modes.默认的系统会采取UIViewContentModeScaleToFill模式,使内容快照拉伸填充满整个View区域。Content Mode的几个形式如下图:
注意,当设置了View的ContentMode后,每一次View的几何形变都会引起系统调用View的drawRect:方法来重绘View,因此应当避免使用该属性,同时对于系统View,我们绝不应该用属性。
View的坐标系统
IOS系统的坐标系统如下所示:
UIWindow, UIView都有自己的坐标系统。对于UIView常用的属性Frame,Bound,Center其属性是相对于不同坐标系统的。具体如下:
frame
, bounds
, and center
properties:
- The
- property contains the frame rectangle, which specifies the size and location of the view in its superview’s coordinate system.
- The
- property contains the bounds rectangle, which specifies the size of the view (and its content origin) in the view’s own local coordinate system.
- The
总结一下,就是view的frame,center属性其坐标系统均是对于其superview的坐标系统来说的。而bounds属性,则是对其自己的坐标系统来说的。
Frame, Center, Bounds的相互关系
Frame,Center都是相对view的superview坐标系统来说的,所以可以用作subview在superview中的定位与大小。对于位移运动,推荐改变Center属性来实现,因为Frame属性在一些变形中是不存在的。
Frame,Center,Bounds互相影响着对方,具体如下:
- When you set the
frame
- property, the size value in the
bounds
- property changes to match the new size of the frame rectangle. The value in the
center
- When you set the
center
- property, the origin value in the
frame
- When you set the size of the
bounds
- property, the size value in the
frame
View的Runtime交互模型
我们可以通过触摸等动作,和view进行实时的交互。
IOS系统中,用户动作与View的交互模型大致如下:
1、用户触摸屏幕,该事件硬件识别,并被发给了UIKit Framework。
2、该触摸事件被UIKit Framework包装成UIEvent类对象,并发给对应的事件响应View。
3、在事件响应View中,我们可以通过对应的函数,来捕获到当前的UIEvent事件(手势识别机制或重写touch系列响应函数)。
4、在View中我们自定义代码做出响应,如:
调用
setNeedsLayoutlayoutSubviews
- )
- 调用
setNeedsDisplay
- or
setNeedsDisplayInRect:drawRect:
- )函数。
- 更改UIView的属性,或通知某个controller对象。
关于View使用的Tips
Apple官方对View的使用做出若干建议,个人感觉比较重要的摘要如下:
1、尽量设置View的Opaque属性为YES。当View的Opaque属性设置为YES时,UIKit框架就不会在去检查View后面是否有可以渲染的东西(因为你已经明确说明该View是不透明的),提高UIKit框架渲染速度。
2、不要在已有的UIControlr对象中添加subview。我们对于UIControl对象的使用,应该尽量保持原生态。虽然对UIControl添加自己的subview技术是可行的,但这么做是危险的。因为当Apple更新版本时,UIControl对象的实现细节可能会改变,而导致我们添加了subview的程序运行失败。
参考资料:
Apple官方文档
View Programming Guide for iOS
https://developer.apple.com/library/prerelease/ios/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW16