View Programming Guide for IOS:View and Window Architecture笔记

来源:互联网 发布:数据库实时同步工具 编辑:程序博客网 时间:2024/05/16 08:17
View 和 Core Animation layers 连结起来渲染和动画view的内容。每个UIKit框架内的view的背后都是一个layer对象(一般是CALayer的实例),这个layer管理view背后的存储并且操作跟view有关的动画。大部分的操作都可以通过UIView的接口来执行,但是如果需要更多的操作来渲染或给视图做动画,也可以直接对layer做操作。

在layer 对象的背后就是CoreAnimation渲染对象和最终用来把真实的内容显示在屏幕上的的硬件缓存。

对Core Animation layer对象的时候对性能很重要。最终的画图代码越简单越好。当画图代码执行后,结果会被Core Animation缓存并尽可能的被重复使用。重复使用已经渲染的内容可以有效消除更新视图的高消耗。特别是在动画执行当中,重复使用内容对性能更重要。这种复用比重新创建的消耗会小很多。

视图层级和子视图管理
通常,子视图的内容覆盖全部或者一部分的父视图。如果子视图是完全不透明的,那么子视图完全覆盖父视图的相应区域;如果子视图是部分透明的(alpha不为1),那么子视图和相应区域内的其他视图混合后在显示在屏幕上。

视图绘画循环
UIView类用on-demand(命令)绘画模型来展现内容。当一个视图第一个展现在屏幕上,系统请求绘出内容。系统保存此次绘画截图并在后续重复使用。如果需要修改内容,需要主动去出发视图的绘画命令。
当视图的内容改编,你不需要直接调用重绘方法。而是应该调用setNeedsDisplay或者setNeedsDisplayInRect:方法。这两个方法告诉系统,视图的内容需要在下一个循环重绘。系统等待当前runloop结束然后执行重绘操作。这个延迟让我们可以一次改变几个视图,并在同一个时间重绘在屏幕上。
注意:改变一个视图的参数并不一定导致系统重绘这个视图。视图的contentMode参数决定了什么参数改变可以导致重绘。

当重绘视图的内容时,绘图的步骤取决于视图和视图的配置。系统视图一般执行私有绘图方法来渲染内容。为了自定义视图,你可以重载drawRect:方法来自定义绘图。同时还有其他的途径可以绘制视图的内容,比如直接设置视图的layer,但是重载drawRect:方法是最普遍的技术。

Content Modes
可以设置UIView的Content Modes参数为UIViewContentModeRedraw,这会使UIView在每次scaling和resizing中重绘。尽量避免使用这个参数。要是用的是标准视图(非自定义视图),不建议使用这个类别。

Stretchable View
我们可以指定视图的部分内容为可拉伸的,因此当视图的尺寸改变的时候,只有可拉伸部分受影响。我们通过contentStretch参数来制定拉伸区域。
视图的contentStretch参数是否起作用也跟contentMode参数有关。只有当contentMode参数为

UIViewContentModeScaleToFillUIViewContentModeScaleAspectFit, 和 UIViewContentModeScaleAspectFill时,contentStretch参数才起作用。

注意:contentStretch参数在ios6系统之后已经不被推荐使用。可以使用UIImage的resizableImageWithCapInsets方法来完成填充背景的工作。


Frame,Bounds和Center的关系。

Frame和center主要用来生成当前视图的形状。比如,你用这些参数来构建视图链或者改变视图的位置和大小(在运行时)。如果只是改变视图的位置(而没有大小),推荐使用center参数。center的值在大部分时候有效,即使改变了视图的大小和方向;但这些情况下frame会变。


clipsToBounds默认为NO。也就是说,当子视图的区域超过父视图的区域时,仍然可以显示超出父视图的区域。我们可以把clipsToBounds设置为YES来避免这种情况。但是,不管clipsToBounds设置为yes或no,在父视图意外区域的点击事件都不会被传导给父视图或子视图。


坐标系统转换

注意:当修改视图的transform参数,所有执行的操作都是与视图的center point相关联的。

/* Return a transform which translates by `(tx, ty)':

     t' = [ 1 0 0 1 tx ty ] 

     其实tx,ty就是视图在x,y轴的位移。

*/

CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,

  CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


/* Return a transform which scales by `(sx, sy)':

     t' = [ sx 0 0 sy 0 0 ]

    其实,sx,sy就是视图在x,y轴的scale

 */

CG_EXTERN CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)

  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


/* Return a transform which rotates by `angle' radians:

     t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] 

    其实,angle就是视图旋转的角度*/

CG_EXTERN CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)

  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


以下都是视图矩阵的变换。​

/* Translate `t' by `(tx, ty)' and return the result:

     t' = [ 1 0 0 1 tx ty ] * t */

CG_EXTERN CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t,

  CGFloat tx, CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

/* Scale `t' by `(sx, sy)' and return the result:

     t' = [ sx 0 0 sy 0 0 ] * t */

CG_EXTERN CGAffineTransform CGAffineTransformScale(CGAffineTransform t,

  CGFloat sx, CGFloat sy) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

/* Rotate `t' by `angle' radians and return the result:

     t' =  [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t */

CG_EXTERN CGAffineTransform CGAffineTransformRotate(CGAffineTransform t,

  CGFloat angle) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

/* Invert `t' and return the result. If `t' has zero determinant, then `t'

   is returned unchanged. */

CG_EXTERN CGAffineTransform CGAffineTransformInvert(CGAffineTransform t)

  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

/* Concatenate `t2' to `t1' and return the result:

     t' = t1 * t2 */

CG_EXTERN CGAffineTransform CGAffineTransformConcat(CGAffineTransform t1,

  CGAffineTransform t2) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


注意:如果视图的变换属性不是恒等变换,那么视图的frame属性是未定义的并且要被忽视掉。当对视图应用变换,我们必须用视图的bounds和center属性来获取视图的位置和大小。视图内的子视图的frame仍然是有效的,因为他们是和父视图的bounds相关的。


运行时交互模型


运行时交互模型

顺序:

1.用户触摸屏幕

2.硬件传导触摸事件给UIKit框架

3.UIKit框架打包触摸事件为一个UIEvent对象,并传导给正确的视图。(事件传导机制)

4.你的事件处理代码响应这个时间。比如

  1)改变视图或者子视图的参数

  2)调用setNeedsLayout方法来使视图变成需要布局更新

  3)调用setNeedsDisplay或者setNeedsDisplayInRect:方法来使视图标记为需要重绘

  4)通知一个控制器,来使某些数据做更新

当然,这些响应代码都是根据自己的业务来的。

5.如果视图的形状改变了,那么子视图也会跟着改变,并遵循如下规则:

  1)如果配置子视图为autoresizing的,那么UIKit会根据配置规则调整子视图

  2)如果视图执行layoutSubviews方法,UIKit调用这个方法。

       你可以重载layoutSubviews方法,来改变子视图的位置和大小。比如,一个大的滑动区域需要很多子视图,        但是把所有子视图都放在内存中不合适。因此,在执行方法时,视图可以隐藏不在当前屏幕内的子视图或者         重新布置子视图的位置并在新的位置内填入正确的内容。作为这次处理的一部分,视图的布局代码也可以标         记子视图为需要重绘。

6.如果视图标记为需要重绘,UIKit会给视图发送重绘命令。

7.所有的更新会混合视图可视内容的其他部分,并一起发送给绘图硬件来显示。

8.视图硬件传输渲染内容到屏幕上。

注意:之前的这些步骤主要用的是标准系统视图和绘图技术。应用OpenGLES来绘图的应用一般配置一个全屏幕的视图并结合OpenGLES绘图上下文直接绘图。这种情况下,视图仍然可以响应触摸事件,但是,因为是全屏幕视图,视图不需要重新布局子视图。


在滚动的时候调整你的绘图行为

滚动可以在短时间内产生大量的视图更新。如果您的视图的绘制代码没有适当调整,为您的视图滚动的表现可能是缓慢的。而不是试图以确保您的视图的内容是原始的在任何时候,请考虑在滚动操作开始时更改您的视图的行为。例如,滚动正在进行中,你可以暂时降低你呈现的内容的质量或改变内容模式。当滚动停止,然后你可以回到你的观点到以前的状态,并根据需要更新的内容。


不要给自定义Controls添加子视图

尽管技术上可以给Control的子类add子视图,但是不建议这样做。比如给UIButton控件增加一个图片,这种行为会在不知道什么时候导致错误。

0 0
原创粉丝点击