Core Animation学习总结

文件夹:
  1. The Layer Beneath
    1. The Layer Tree(图层树)
    2. The Backing Image(寄宿层)
    3. Layer Geometry(图层几何学)
    4. Visual Effects(视觉效果)
    5. Transforms(变换)
    6. Specialized Layers(专有图层)
  2. Setting Things in Motion
    1. Implicit Animations(隐式动画)
    2. Explicit Animations(显式动画)
    3. Layer Time(图层时间)
    4. Easing(缓冲)
    5. Timer-Based Animation(基于定时器的动画)
  3. The Performance of a Lifetime
    1. Tuning for Speed(性能调优)
    2. Efficient Drawing(高效画图)
    3. Image IO(图像IO)
    4. Layer Performance(图层性能)



Core Animation 之 CALayer
The Layer Tree(图层树)

一、图层与视图的差异:
  • 视图是以树的数据结构来管理层级关系的。而图层相同也是以树的数据结构来管理层级关系
  • 视图在程序中以UIView及其子类来表示。而图层以CALayer及其子类专用 图层来表示
  • UIView老是与CALayer是一一相应的关系,因此本质上,iOS上界面上的内容的呈现与动态其实是经过CALayer来实现的,而UIView是封装了CALayer的基础上加入了事件响应、布局等高级功能。


二、关于视图层级、图层树、呈现树、渲染树
  • 视图层级:主要负责实现事件响应、布局等功能,因为视图封装图层。图层向视图暴露部分编程接口与属性,因而经过改动视图的效果可以间接改动图层的属性。

  • 图层树:负责定义界面图形的绘制、动画效果,iOS是有一个绘制周期的——60FPS,也就是说图层树负责保存这个周期内的相关属性的改动,而到了周期结束要进行绘制的时候,才把图层树的数据模型更新到呈现树中。
  • 呈现树:肯定当前屏幕上界面图形的详细效果。用于保存当前屏幕的图形的显示属性,每隔一个绘制周期和图层树同步一次。
  • 渲染树:iOS系统会专门建立一个进程来运行图形渲染的任务,当随意APP(包含系统App)需要图形渲染的时间,它们就会把渲染任务发送到该线程去运行渲染。

因此以上四层。每一层都负责不一样的任务,其数据流是视图-》图层-》呈现树-》渲染树。

并且绘制周期60FPS也是会随着系统的状态而变化的,若系统资源超载,而会经过下降绘制次数来确保系统能稳定执行。html


三、视图动画
视图动画是显式,因为UIView默认把CALayer的隐式动画禁止掉了。因此要经过动画Block的形式才干取消动画的限制。(动画block实际上就是曾经的动画栈)。
并且视图能实现的动画仅仅是图层所开放的一部分,都是2D动画,因此视图动画的效果会稍稍简单。

四、图层动画
图层动画是隐性动画。仅仅要改动图层的属性。其变化都会以默认的渐变形式呈现出来(详细原理见《隐式动画》)。并且图层动画支持不少其它视图动画没法实现的效果。如:
  • 阴影、圆角、带颜色的边框
  • 3D变化
  • 非矩形范围
  • 透明遮罩
  • 多级非线性动画。

五、更适宜使用CAlayer呈现内容的场景
因为UIView与CALayer都可以呈现内容。尽管CALayer不能直接实现事件响应。但开发人员也可以经过hit-test机制的原理来本身实现事件响应,那什么场景更加适合用CALayer而不是UIView呢?例如如下所看到的:
  • 开发同一时候可以在MAC、iOS上执行的跨平台应用
  • 使用多种CALayer的子类(专有图层),并且不想建立额外的UIView去封装
  • 作一些对性能特别挑剔的工做,如对UIView一些可忽略不计的操做都会引发显著的不一样(也可以经过OpenGL来解决)。


The Backing Image(寄宿层)

一、寄宿层&Contents属性
CALayer有一个名曰Contents的属性,这个属性是与寄宿层相相应了,而contents属性指向的对象必须为CGImageRef类型(一个指向CGImage结构的指针),因此寄宿层是用来展现图片所用的。例如如下状况会调用寄宿层:
  • 显示图片
  • core Graphics
core Graphics可以实现本身定义绘制,但一般不建议那么作,因为core Graphics绘制会默认生成一个绘制专用的存储空间,而这空间有十多M那么多。会占用大量内存,因此Apple不建议实现空的core Graphics更不建议在core Graphics实现不属于该方法的代码。

二、contentGravity属性
与UIView的contentMode属性相相应,用于调整图片的布局,支持一下常量值:
  • kCAGravityCenter
  • kCAGravityTop
  • kCAGravityBottom
  • kCAGravityLeft
  • kCAGravityRight
  • kCAGravityTopLeft
  • kCAGravityTopRight
  • kCAGravityBottomLeft
  • kCAGravityBottomRight
  • kCAGravityResize
  • kCAGravityResizeAspect
  • kCAGravityResizeAspectFill

三、contentsScale
contentsScale属性定义了寄宿层的像素尺寸和视图大小比例,默认是1.0(一个点一个像素),在retina屏幕得设置在2.0(一个点两个像素)。在plus设备上得设备为3.0(一个点3个像素)。
但若contentGravity设置了如kCAGravityResizeAspectFill本身主动适配大小的属性后,contentsScale会不起做用。因此不能依靠contentsScale来作缩放的操做,缩放仍是得交给transform或者affineTransform。


三、maskToBounds
该属性与UIView的clipsToBounds属性相相应。都是应用于将超出图层边界的子图层的内容进行裁剪。

这里需要一点需要注意的,但咱们实现radiusCorner时,实际上就是经过设置背景颜色来实现的。而与maskToBounds结合才是真正把边角内容裁剪掉,但radiusCorner+maskToBounds结合使用会引起离屏渲染。因此在radiusCorner知足要求。就不要再调用maskToBounds。

四、contentsRect
CALayer的contentsRect赞成咱们在图层边框内显示寄宿图的一个子域,而contentsRect是一个相对值属性,如{0,0。1,1}表示整个寄宿图,这样的坐标系统也叫单位。例如如下简述iOS下的三种坐标系统单位:
  • 点:点是虚拟的像素,也叫逻辑像素。在不一样的设备上一个点所表明的像素点是不同的。如普通屏幕一个点就是一个像素,而retina屏幕一个点就是两个像素。
  • 像素:实际的物理像素坐标。
  • 单位:一种相对的坐标,优势就是应用相对值,方便移植。
contentsRect有一种经典的使用方法——image sprites(图片拼接,常用于游戏),这种使用方法就是用来实现一次性载入多张图片的。因为每一张图片的使用前都要通过,载入——》压缩——》呈现,的一个过程中载入和压缩是十分耗时的,载入消耗IO,压缩算法的复杂会添加CPU的消耗,因此想游戏这种App需要载入解压大量的图片时,可以把所有图片整合成一张图片来载入解压,而后经过contentsRect裁剪出子图,并显示,可以优化载入解压的过程。

五、contentsCenter
contentsCenter的名字事实上有一点的误导性,事实上际上与UIView的Stretching属性相相应。用来实现寄宿层的缩放时的呈现效果。也是一个相对值单位。


六、customs drwaing
除了经过CGImage设置到contents的方式来实现寄宿层。还可以经过Core Graphics来直接绘制寄宿层,但这样的方式十分消耗内存不建议使用。
CALayer有一个可选的Delegate属性。实现了CALayerDelegate,但CALayer需要又一次绘制,则调用它的displayLayer方法(与UIView的setNeedDispaly相相应),就会调用其代理方法drawLayer:inContext(与UIView的drawRect相相应)。

若在视图层中,仅仅要用户实现了drawRect那么仅仅要屏幕需要又一次绘制那么该方法都会被默认调用。git


Layer Geometry(图层几何学)

一、布局
UIView的三个重要布局属性:frame、bounds、center
CALayer的三个重要的布局属性:frame、bounds、position
可以看出UIView与CALayer的布局属性是一一相应的,而惟一有啥区别的就是锚点的命名,UIView为center,而CALayer为position,但锚点最開始都是默以为图形的中心。
当中frame实际上就是一个虚拟属性,其由bounds与center/position计算所得。因此改动bounds与center/position也会影响到frame的数值。
还有无论是视图仍是图层。在屏幕上无论怎么变形,本质上其仍是一个矩形。但在旋转的状况下。frame的数值是会事实变更的,例如如下图:



二、锚点
锚点属性是用来指定图层相对于父图层的位置。也可以理解为利用图层的句柄。默以为图层的中心但也可以改动。在图层中改动了锚点会产生移动(隐性动画),相同的锚点也是一个相对值。
例如如下。为锚点的实验效果图:

图一:时分秒针直接以图层的中点为锚点,因为旋转时也以其为中心,效果例如如下:


图二:将锚点改动成指针的尾端


图三:改动锚点后的旋转效果。


三、坐标系
因为每个图层都有本身的坐标系,因此CALayer也提供了一系列的方法用于一个点在不一样的坐标系之间转换,这些方法特别适合于子图层与父图层之间的坐标转换。




事实上UIView与CALayer都有zPosition。但由于UIView只支持2D变换,因此UIView的zPosition只适合于用来做图层的调整,但更加建议用UIView提供的视图数组管理方法来调整视图的绘制顺序来调整。而CALayer的zPosition是一个重要的三维坐标系。

四、Hit Testing
Hit Testing是iOS中一个十分重要的机制,用于检索点击了的目标图标,与响应链条相互搭配的话。就会将终于目标图标设置为第一响应者。Hit Testing提供了两个核心的方法:
  • containPoint:(用于直接推断某个点是否属于某个图层)
  • -hitTest(经过递归遍历子对象的方式来直接返回目标图层)

五、本身主动布局
对于UIView。经过UIView暴露出来的UIViewAutoresizingMask和NSLayoutConstraint来实现本身主动布局。

但对于CALayer,则需要手动来操做实现,当中最为简单的方法就是实现CALayerDelegate的例如如下函数:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
在该方法内依据当前layer的属性来计算调整来实现本身主动布局。

因为CALayer实现本身主动布局不方便,因此这也是为何更加建议使用UIView来实现界面的构建。

Visual Effects(视觉效果)——本节探讨能够经过CALayer实现的附加视觉效果

一、圆角
CALayer有一个叫作cornerRadius的属性控制图层角的曲率,这个曲率仅仅影响背景颜色而不影响图片或者子图片。圆角的计算原理就是一个以CALayer的中点,曲率所设的半径的原与矩形的交集就是目标图形。
另外一个名曰maskToBounds属性,就是实现将图层里面边界外的图形全部分割丢弃。
因此经过cornerRadius+maskToBounds组合可以实现圆角,但同一时候会引起离屏渲染,如果小量的离屏渲染还好,但如滚动栏的场景,开会有大量的离屏渲染的任务产生,就会严重影响性能,这一点也是注意优化的。详细效果例如如下图所看到的:




二、图层边框
经过设置CALayer的borderWidth与borderColor两个属性可以改动边框的效果。

效果例如如下:算法





三、阴影
阴影是一种能达到图层深度暗示效果的装饰。

CALayer提供下面參数来定制阴影的效果:数据库

  • shadowOpacity:设置一个大于零的数值,那么阴影就会显示在图层如下(默以为0)。
  • shadowColor:设置阴影的颜色(默认黑色)
  • shadowOffset:设置阴影的偏移量(默认{0。-3})
  • shadowRadius:设置阴影模糊度,数值越大。阴影的模糊度越高(默以为0)。曲率越大,阴影效果越明显。
shadowRadius的设置效果图例如如下:


有一点需要注意的,就是阴影的绘制是属于离屏绘制。是比較效果资源的。因此必定要下降同一屏幕进行大量阴影绘制的状况。


四、阴影裁剪
图层的阴影不少其它是继承于内容的外形,而不是依据边界和角半径来肯定。为了计算出阴影的形状。core animation会将寄宿层也考虑在内。

但若直接maskToBounds来裁剪,会把阴影效果也裁减掉。编程

为了解决以上问题。可以经过专门引入一个阴影图层来解决,详细效果例如如下:

图一:直接maskToBounds裁剪会把阴影效果一并裁剪掉。


图二:经过加入一个另外的阴影图层在如下,然它来实现阴影。而详细的内容图层就直接maskToBounds裁剪。


图三:终于既能实现裁剪,又能实现阴影。


PS:无论是阴影仍是圆角裁剪都会引起离屏渲染,大量的该类型的图形需要渲染的话。会减低帧率。


五、shadowPath 属性
阴影并必定是方形的。咱们也可以经过shadowPath来定制本身想要的阴影形状。详细效果例如如下所看到的:




六、图层蒙版
经过masksToBounds属性。咱们可以实现边界裁剪,但咱们还需要更加灵活的裁剪需求的时候就可以经过图层蒙版来实现。CALayer提供一个mask的属性来解决一个问题。而mask原本就是指向还有一个图层的指针。其详细原理例如如下图所看到的:




从上图效果可以知道mask舒心仅仅关心形状的交集,而颜色仍是由原来的图形的颜色决定。

另外一点需要注意的是图层蒙版也会引起离屏渲染,会带来性能问题,因此使用的时候也得多加注意。


七、拉伸过滤
当咱们视图显示一个图片的时候。都应该以正确的比例,正确的1:1像素显示在屏幕上。缘由例如如下:
  • 能够显示更好的画质,像素既没有被压缩也没有被拉伸
  • 能更好使用内存,因为这就是你要存储的东西
  • 最好的性能表现,CPU不需要为此额外的计算 
但有时候咱们就需要缩略图。所专门另外存储缩略图这对内存来讲。

这时就可以经过CALayer的minificationFilter(缩小滤波器)和magnificationFilter(方法滤波器)属性实现图形的缩放算法。当中提供下面三种默认的缩放算法:数组

  • kCAFilterLinear(双线性滤波算法,默认算法)
  • kCAFilterTrilinear(三线性滤波算法)
  • kCAFilterNearest(近期滤波算法)
对于大图的缩放,採用kCAFilterLinear、kCAFilterTrilinear,效果会比較好。

对于小图、较少斜线的图的缩放,。採用kCAFilterNearest效果比較好。

图一:採用kCAFilterLinear的方法滤波器算法的效果。


图二:採用kCAFilterNearest的放大滤波器算法的效果:




八、透明组
UIView有一个alpha属性类肯定视图的透明度。而CALayer有一个与之相应的属性——opacity。这两个属性都会影响到子图层。例如如下图所看到的:
当咱们将view2的alpha数值设置为0.5,这时候,对于label所处的点的颜色计算公式为:50%label + 25%view + 25%background,因此就是灰色偏白的颜色。
若想整个图层的色调保持一致,可以经过将shouldRasterize属性设置为YES,那么图层及其子图层将被整合成一个整体的图片。效果例如如下:

图一:shouldRasterize属性设置为默认值NO的效果。


图二:shouldRasterize属性设置为默认值YES的效果。




Transforms(变换)

一、仿射变换
UIView相应的transfrom属性是一个CGAffineTransfrom类型。用于实现二维空间旋转、平移、缩放、斜切。
而CALayer的transform属性是一个CATransforms3D。能应用3维空间进行旋转、平移、缩放、斜切等效果。
当中,仿射变化的数学原理例如如下:


只是iOS为了运算的方便。已经提供了三个标准方法分别用于实现旋转、缩放。平移,而斜切得靠原生运算来实现。


当中有一点需要注意的,就是旋转的angle是以弧度制为单位的。

iOS系统提供下面方法来实现混合变换,能同一下面的组合函数实现复杂的变形模式:

图一:建立一个空的变换结构体。




图二:不断叠加变换结构体


图三:直接将两个变换结构体合并


剪切变换的效果与事实上现代码:




二、3D变化
3D变化与2D变化是十分类似的,不一样的仅仅是3D变换是3个维度的,加入了Z轴。其数学原理例如如下:


这样的矩阵运算仍是挺麻烦的。因此iOS系统提供给咱们便捷的方法:


相同也会有变换叠加的方法,当中旋转的时候必定要注意当前的旋转的參考轴,顺时针旋转为正值,逆时针旋转为负值。




三、透视投影
在真实的世界中,当物体远离咱们。由于视角的缘由,其会变小。因此为了让3D效果更佳真实,需要引入投影变换,尽管core animation并无提供给咱们,但十分简单,其数学实现例如如下:


仅仅要将m34 = -1/d, d= 500~1000,(d越小越失真,d越大透视效果越弱)。


图3.1:没实现透视投影的旋转效果。



图3.2:实现了透视投影的旋转效果。



四、灭点
在人的视觉中。当物体离人越远则会变得越小,当接近无穷远的时候就会汇聚成一个不可见的点,这个点就叫灭点。现实生活中,这个点一般就是中心点,因此在程序中咱们也要让图形的灭点集中在屏幕的中点,例如如下所看到的:


而实现方式则是。首先加入图形的时候先以屏幕中点为锚点加入,而后经过transfrom来说图层移动到恰当的位置。


五、sublayerTransform
sublayerTransform是一种将所有对应的配置统一设置到该图层的所有子图层的便捷属性。

六、3D坐标下的图层背面
在3D坐标下,图层的背面也是会绘制的,而且是正面的镜像。
咱们可以经过CALayer的doubleSided属性来决定是否运行双面绘制。


七、扁平化图层
假设父图层的坐标发生变换,那么子图层也会随着父图层而进行相同的变换,如7.1图所看到的。但有时咱们仅仅想变换父图层并不想变换子图层。这时就得作相对变换来抵消掉父图层的变换。让视觉效果看起来子图层是精巧的。例如如下图所看到的:
图7.1:2D相对旋转理论图


图7.2:2D相对旋转效果图


图7.3:3D相对旋转理论图


图7.4:3D相对旋转实际效果图


咱们可以看出,在2D坐标系下。相对变换是合理的。但在3D坐标系下,相对变换的结果并不如预想的同样,为何呢?
这是因为core Animation图层尽管存在于3D空间以内,但并不是每一个图层都存在于同一个3D空间之间。

而用户是以当前window所在的3D坐标系为參考的,这时7.4图的3D坐标系与window的已经不重合的,因此在咱们的视觉里面,就是看不到预想的效果,这就是图层扁平化。缓存

图层扁平化让core animation建立复杂的3D场景变得十分困难,因为很是难使用图层树去建立一个3D结构的层级关系——将所有的场景下的3D都保持一样的3D坐标系。
只是CALayer提供一个专用图层——CATransformLayer来解决问题,所有加入到该图层内的3D图形都共享一个标准坐标系。



八、3D场景下的光亮和阴影
略。临时不懂。

九、3D场景下的点击事件
在处理点击事件,必定要注意点击响应是由UIView中的图层结构来决定的,而不是CALayer在屏幕上的呈现效果,因此得注意事件拦截的顺序。
可以经过设置userInteractionEnabled以及简单的视图覆盖来实现点击事件正确的传递。


Specialized Layers(专有图层)

一、CAShapeLayer


二、CATextLayer


三、CATransformLayer


四、CAGradientLayer


五、CAReplicatorLayer


六、CAScrollLayer


七、CATiledLayer


八、CAEmitterLayer


九、CAEAGLlayer


十、AVPlayerLayer


Core Animation 之 动画
Implicit Animations(隐式动画)

一、隐式动画&事务
Core Animation基于一个若是,就是屏幕上的所有图形都可以作动画,而且这样的动画不需要开发人员去设置,会本身主动实现。这也是为何直接改动CALayer的属性会以一种渐变的形式来更新到一个新的值。
这样的仅仅需要改动图层的数值就会引起动画的形式来更新到新数值的动画称为隐式动画。
而这样的定义了动画的详细呈现效果的机制,就称为事务。
由于CALayer是默认开启了隐式动画的。而若咱们在CALayer上调用显式动画的话,就考虑到隐式动画对效果的影响,进而决定是否需要取消隐式动画。

二、动画Block
UIView封装了CALayer。但UIView禁止了隐性动画,因此开发人员需要在UIView上实现显性动画。

在iOS4.0以前,UIView採用动画栈来管理显示。后来提供了动画Block这个更加便捷的方式。但原理都是同样的。性能优化


三、图层行为
咱们把改变CALayer属性时本身主动应用的动态称为行为。一组行为的运行步骤例如如下所看到的:
  1. CALayer调用-actionForKey:方法,传递属性名称。
  2. 检查CALayer是否持有实现了CALayerDelegate的代理对象(需要实现actionForLayer:forKey方法),如有直接调用并返回结果,若无继续下面步骤。
  3. 检查包括属性名称相应行为映射的actions字典。如有则返回,无则继续
  4. 在style字典接着搜索属性名。如有这返回,无则继续
  5. 调用属性的标准行为defaultActionForKey方法
通过以上的整个流程,要么actionForKey返回nil则无动画效果,要么就是返回CAAction,而后CALayer利用它实现动画。
所以可以推进出UIView是怎样禁止隐式动画的,就是讲CALayer的delegate设置为自身。而后在actionForLayer:forKey方法中返回nil。

  • 禁止隐式动画的两个方法:
    • 依据图层行为的原理,实现代理方法actionForLayer:forKey强制返回nil。
    • 经过设置[CATransaction setDisableActions:YES]来实现禁止隐式动画

四、呈现与模型
在iOS中。屏幕每秒钟又一次刷新屏幕60次。

因此Core Animaiton就需要在设置一次新值和新值生效之间,对屏幕上的图层进行又一次组织。微信

正是觉得上面的机制的存在。因此才会有iOS才会有图层以及呈现层之间的关系,图层更像是model,而呈现层就是view,Core Animation就是Controller,因此在一个绘制周期内,图层负责收集与保存用户对属性的改动,到绘制时刻时。就将图层的数据覆盖到呈现树。

咱们可以经过CALayer的 -presentationLayer方法来获取正在屏幕上显示的呈现树的数据,尽管通常不需要。但在下面状况下会比較有做用
  • 作基于定时器的动画时。而不不过基于事务的动画。这时准备地知道当前时刻的图层的显示位置是十分实用的
  • 假设你想作图层的响应输入,可以经过-hitTest方法来推断制定图层是否被触摸。这时候对呈现图层调用-hitTest会更加有效,因为呈现树就是当前屏幕上的图形实际的位置。


Explicit Animations(显式动画)

一、Core Animation的类图架构


  • CAAnimation实现了CAMediaTiming协议。自己并无作什么工做。主要实现动画的时间相关的属性以及计算函数(缓冲)
  • CATrasition描写叙述变换的对象
  • CAAnimationGroup封装属性动画的队列。
  • CABasicAnimation基础属性动画
  • CAKeyFrameAnimation关键帧动画

二、基础属性动画
基础动画主要由CABasicAnimation实现,由上图可知道。而CABasicAnimation继承于CAPropertyAnimation属性动画,经过设置下面三个数值以及其它的选项,就能够实现本身主动动画:
  • fromValue
  • toValue
  • byValue
当中toValue与byValue不能同一时候使用,toVaule是绝对值,byValue是相对值。

属性动画都是针对关键帧的动画。也就是说仅仅关心出发点与结束点,中间的过渡可以本身生成也可以默认。网络


三、关键帧动画
CAKeyFrameAnimation用于实现关键帧动画,能经过设置其animations数组来定义每个关键帧的位置,也可以直接经过path属性来定义运动的路径。


四、affineTransform属性
若咱们让一个图形沿着曲线运动。可以经过设置affineTransform。让其能沿着曲线的切线运动从而让运动更加真实。先后效果图例如如下图所看到的:



五、虚拟属性
属性动画其实是针对关键路径而不是一个键的。这就意味着可以对子属性甚至虚拟属性作动画。

如若咱们想实现旋转的效果,原本咱们需要在keyPath上设置“transform”,
若使用虚拟属性。可以直接在keyPath上直接设置“transfrom.rotation”,这样在设置formValue与toValue时就可以直接设置弧度叫的值。
事实上transfrom.rotation这个属性是并不存在的,而是core aniamiton本身主动依据CAValueFunction来又一次计算transform的数值所得,正因为如此才称之为虚拟属性。


六、动画组
CAAnimationGroup是一种组合动画的解决方式。经过CAAnimationGroup加入沿曲线运动与颜色变化的动画。



七、过渡
略。


八、在动画的过程当中取消动画
Core Animation提供了一部分的方法来实现动画加入以及移除。例如如下图所看到的:
- (CAAnimation *)animationForKey:(NSString *)key;  //实现动画的加入,当中key不只可以用来訪问动画,还可以用来移除动画
- (void)removeAnimationForKey:(NSString *)key;  //移除Key指定的动画
- (void)removeAllAnimations;  //移动CALayer已经加入的所有动画
Layer Time(图层时间)

一、CAMediaTiming协议
CAMediaTiming协议定义了在一段动画内控制逝去时间的属性集合。

CALayer和CAAnimaition都实现了该协议,因此时间可以被随意图层或者一段动画的类所控制。


CAMediaTiming协议下定义的一些核心属性:
  • duration:定义一段动画的持续时间
  • repeatCount:定义一段动画的反复次数
  • repeatDuration:制定动画反复一段制定的时间
  • autoreverses:制定在每次间隔交替循环的过程当中本身主动回放
可以经过将repeatCount或者repeatDuration设置为INFINITY来实现动画无限循环播放。但不能同一时候使用这两个属性。


二、相对时间
在Core Animation中,时间是相对的,每个动画都有本身的描写叙述时间,可以独立的加速、延迟或者偏移,当中有下面关键属性:
  • beginTime:动画的開始时间。但不是绝对时间,而是一个相对时间,就是从该动画加入到可见层的那一刻開始測量,默认是零。
  • speed:时间倍速,默认是1.0,若设置为2.0而二倍速前进。那么对于一个duration为1.0的动画,实际上0.5s就已经完毕。
  • timeOffset:让动画直接快进到某个时间点进行,使用它要注意是否有speed的參与而改变的duration
  • duration:动画的播放时间。

三、fillMode
当一个图层产生动画,实际上就是呈现层在运行动画。那么。当呈现层运行完动画是否要讲当前属性的值覆盖会图层。这样的行为就称为fill mode,这个由开发人员决定:
  • kCAFillModeForwards:(保持结束后的值)
  • kCAFillModebackwards:(保持结束前的值)
  • kCAFillModeBoth:(包含以上两种状况)
  • kCAFillModeRemoved:(默认。当不在播放动画时,则显示回图层的属性值)


四、全局时间与本地时间
CALayer 或者 CAGroupAnimation 调整 duration repeatCount / repeatDuration 属性并不会影响到子动画。但是 beginTime timeOffset speed 属性将会影响到子动画。
每个 CALayer CAAnimation 实例都有本身 本地 时间的概念。是依据父图层/动画层级关系中的 beginTime timeOffset speed 属性计算。

CoreAnimation有一个 全局时间 的概念。也就是所谓的 马赫时间 (“马赫”其实是iOS和Mac OS系统内核的命名)。
CACurrentMediaTime 函数来訪问马赫时间:
CFTimeInterval time = CACurrentMediaTime();
该值返回的是一个相对值,与现实的时间无关。但可以经过它来比对不一样动画之间的时间差,iOS提供下面方法来进行不一样图层之间本地时间的转化:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l; 
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
经过以上知识可以实现同步不一样图层的speed,timeOffset、beginTime的动画。


五、暂停、倒回和快进的实现
设置动画的 speed 属性为0可以暂停动画。但不能再加入后再改动。不然会报错。
但咱们可以经过下面的设置来实现一些调试:
self.window.layer.speed = 100;

六、手动动画
将speed设置为0让动画中止播放。而后改动timeOffset来切换不时间的动画序列。从而实现手动切花动画的效果。

Easing(缓冲)——让动画更加平滑天然

一、CALayer、Animation的缓冲动画与CAMediaTimingFucntion
首先需要设置 CAAnimation timingFunction 属性,是 CAMediaTimingFunction 类的一个对象。假设想改变隐式动画的计时函数,相同也可以使用 CATransaction +setAnimationTimingFunction: 方法。来实现缓冲函数。当中有下面默认的缓冲函数:
kCAMediaTimingFunctionLinear            //线性
kCAMediaTimingFunctionEaseIn            //缓进
kCAMediaTimingFunctionEaseOut           //缓出
kCAMediaTimingFunctionEaseInEaseOut     // 缓进 缓出
kCAMediaTimingFunctionDefault           //默认的效果与 kCAMediaTimingFunctionEaseInEaseOut相似,但效果更佳不明显

二、UIView的缓冲动画
经过设置UIView的options參数加入例如如下常量也可以实现缓冲动画的效果:
UIViewAnimationOptionCurveEaseInOut 
UIViewAnimationOptionCurveEaseIn 
UIViewAnimationOptionCurveEaseOut 
UIViewAnimationOptionCurveLinear


三、缓冲和关键帧动画
CAKeyframeAnimation 有一个 NSArray 类型的 timingFunctions 属性。咱们可以用它来对每次动画的步骤指定不一样的计时函数。但是指定函数的个数必定要等于 keyframes 数组的元素个数 减一 ,因为它是描写叙述每一帧之间动画速度的函数。


四、本身定义缓冲函数
临时不讨论。


Timer-Based Animation(基于定时器的动画)——咱们可以经过事务来实现动画。设置关键帧,让中间的过渡本身主动计算得出。也可以基于定时器来设置每一个绘制周期的变换来实现动画。


iOS提供一下两种形式来实现基于定时器的动画:
  • NSTimer  :并不是依据实际帧率来运行,因此CADispaly是更好的解决方式
  • CADispaly  :依据实际的帧率来运行。更加适合

但无论是NSTimer仍是CADispaly本质上都是基于runloop,NSTImer把每个时间触发的事件注冊到runloop里面。以待制定。

而CADisplay则把任务直接嵌套进绘制操做以前。



Core Animation 之 性能优化
Tuning for Speed(性能调优)

一、CPU &  GPU
CPU(中央处理器)和GPU(图形处理器)都是能用来处理。

总的来讲。咱们可以用软件(使用CPU)作不论什么事情,但是对于图像处理,通常用硬件会更快,因为GPU使用图像对高度并行浮点运算作了优化。

因为某些缘由,咱们想尽量把屏幕渲染的工做交给硬件去处理。问题在于GPU并无无限制处理性能。而且一旦资源用完的话,性能就会開始降低了(即便CPU并无全然占用)。

性能优化的本质就会合理地利用CPU与GPU,使他们不会超出负荷。


Core Animation处于iOS的核心地位,无论是应用内仍是应用外都会用到它。因此iOS特别设计了一个进程来运行渲染相关的任务,也叫渲染服务。渲染服务管理动画和屏幕上组合的图层。
当执行一段动画的时候。这个过程会被切分为六个阶段,包含应用内的四个阶段,与应用外的2个阶段。当中应用内的阶段例如如下:
  • 布局(CPU):这是准备你的视图/图层的层级关系。以及设置图层属性(位置,背景色。边框等等)的阶段。
  • 显示(CPU):这是图层的寄宿图片被绘制的阶段。绘制有可能涉及你的-drawRect:-drawLayer:inContext:方法的调用路径。
  • 准备(CPU):这是Core Animation准备发送动画数据到渲染服务的阶段。这同一时候也是Core Animation将要运行一些别的事务好比解码动画过程当中将要显示的图片的时间点。

  • 提交(CPU):这是最后的阶段,Core Animation打包所有图层和动画属性,而后经过IPC(内部处理通讯)发送到渲染服务进行显示。
当数据被打包到渲染服务进程。会将其反序列化造成还有一个渲染树。

使用这个渲染树对动画的每一帧作出例如如下工做:

  • 生成渲染树:(CPU)对所有的图层属性计算中间值。设置OpenGL几何形状(纹理化的三角形)来运行渲染
  • 渲染(GPU):在屏幕上渲染可见的三角形

减小GPU图层绘制的部分场景:
  • 太多的几何结构 - 这发生在需要太多的三角板来作变换,以应对处理器的栅格化的时候。现代iOS设备的图形芯片可以处理几百万个三角板。因此在Core Animation中几何结构并不是GPU的瓶颈所在。但由于图层在显示以前经过IPC发送到渲染server的时候(图层其实是由很是多小物体组成的特别重量级的对象),太多的图层就会引发CPU的瓶颈。这就限制了一次展现的图层个数(见本章兴许“CPU相关操做”)。
  • 重绘 - 主要由重叠的半透明图层引发。GPU的填充比率(用颜色填充像素的比率)是有限的,因此需要避免重绘(每一帧用相同的像素填充屡次)的发生。在现代iOS设备上,GPU都会应对重绘;即便是iPhone 3GS都可以处理高达2.5的重绘比率。并任然保持60帧率的渲染(这意味着你可以绘制一个半的整屏的冗余信息,而不影响性能),并且新设备可以处理不少其它。
  • 离屏绘制 - 这发生在当不能直接在屏幕上绘制。并且必须绘制到离屏图片的上下文中的时候。

    离屏绘制发生在基于CPU或者是GPU的渲染,或者是为离屏图片分配额外内存,以及切换绘制上下文,这些都会减小GPU性能。对于特定图层效果的使用,比方圆角,图层遮罩。阴影或者是图层光栅化都会强制Core Animation提早渲染图层的离屏绘制。

    但这不意味着你需要避免使用这些效果,仅仅是要明确这会带来性能的负面影响。

  • 过大的图片 - 假设视图绘制超出GPU支持的2048x2048或者4096x4096尺寸的纹理,就必需要用CPU在图层每次显示以前对图片预处理,相同也会减小性能。

减小CPU图层绘制的部分场景:
  • 布局计算 - 假设你的视图层级过于复杂,当视图呈现或者改动的时候,计算图层帧率就会消耗一部分时间。特别是使用iOS6的本身主动布局机制尤其明显。它应该是比老版的本身主动调整逻辑增强了CPU的工做。
  • 视图惰性载入 - iOS仅仅会当视图控制器的视图显示到屏幕上时才会载入它。

    这对内存使用和程序启动时间很是有优势,但是当呈现到屏幕上以前,按下button致使的不少工做都会不能被及时响应。比方控制器从数据库中获取数据,或者视图从一个nib文件里载入,或者涉及IO的图片显示(见兴许“IO相关操做”)。都会比CPU正常操做慢得多。

  • Core Graphics绘制 - 假设对视图实现了-drawRect:方法,或者CALayerDelegate-drawLayer:inContext:方法,那么在绘制不论什么东西以前都会产生一个巨大的性能开销。为了支持对图层内容的随意绘制,Core Animation必须建立一个内存中等大小的寄宿图片。而后一旦绘制结束以后,必须把图片数据经过IPC传到渲染server。在此基础上,Core Graphics绘制就会变得十分缓慢。因此在一个对性能十分挑剔的场景下这样作十分很差。
  • 解压图片 - PNG或者JPEG压缩以后的图片文件会比同质量的位图小得多。但是在图片绘制到屏幕上以前,必须把它扩展成完整的未解压的尺寸(一般等同于图片宽 x 长 x 4个字节)。

    为了节省内存。iOS一般直到真正绘制的时候才去解码图片(14章“图片IO”会更具体讨论)。

    依据你载入图片的方式,第一次对图层内容赋值的时候(直接或者间接使用UIImageView)或者把它绘制到Core Graphics中。都需要对它解压。这种话,对于一个较大的图片,都会占用必定的时间。

二、Instruments实现App性能优化,操做顺序一般例如如下:
  • 加入优化工具
  • 选择福选项。设置感兴趣的数据
  • 对照測试得结果
Efficient Drawing(高效画图)

一、软件绘制
软件画图意为不借助GPU的图形绘制。在iOS中一般就是由Core Graphics来实现。但其对照Core Animation和OpenGL,Core Graphics要慢很多,而且也十分消耗内存。
软件画图不只效率低,还会消耗可观的内存。 CALayer 仅仅需要一些与本身相关的内存:仅仅有它的寄宿图会消耗必定的内存空间。即便直接赋给 contents 属性一张图片,也不需要添加额外的照片存储大小。假设一样的一张图片被多个图层做为 contents 属性,那么他们将会共用同一块内存,而不是复制内存块。

但是一旦你实现了 CALayerDelegate 协议中的 -drawLayer:inContext: 方法或者 UIView 中的 -drawRect: 方法(事实上就是前者的包装方法),图层就建立了一个绘制上下文,这个上下文需要的大小的内存可从这个算式得出:图层宽*图层高*4字节。宽高的单位均为像素。对于一个在Retina iPad上的全屏图层来讲。这个内存量就是 2048*1526*4字节,至关于12MB内存,图层每次重绘的时候都需要又一次抹掉内存而后又一次分配。

软件画图的代价昂贵,除非绝对必要。你应该避免重绘你的视图。

提升绘制性能的秘诀就在于尽可能避免去绘制。



二、矢量图形

咱们用Core Graphics来画图的一个一般缘由就是仅仅是用图片或是图层效果不能轻易地绘制出矢量图形。矢量画图包括一下这些:

  • 随意多边形(不不过一个矩形)
  • 斜线或曲线
  • 文本
  • 渐变
文章中实现了例如如下效果的demo:


事实上现思路就是每当有touch event发生,则向UIBezierPath中加入一条直线。但在对于屏幕中每当有一个touch event事件发生,意味着整个屏幕都要又一次绘制一遍,当需要又一次绘制的内容愈来愈多,则会引发帧率的降低。

这时候咱们可以经过脏矩阵来对这个问题进行优化。


三、脏矩形
脏矩形是一个能制定又一次绘制区域的机制。

在程序中则是经过用setNeedDispalyInRect来替代setNeedDispaly方法来实现绘制详细的区域,详细实现见文章。


四、异步绘制
UIKit的单线程天性意味着寄宿图一般要在主线程上更新,这意味着绘制会打断用户交互,甚至让整个app看起来处于无响应状态。咱们对此无能为力,但是假设能避免用户等待绘制完毕就好多了。

针对这个问题,core Animation提供了一下两种方案来实现异步绘制,提升界面的响应效率:
  • CATiledLayer(详细实现可见第六章)
  • drawsAsynchronously(????)



Image IO(图像IO)——研究怎样优化从闪存或者网络中载入和显示图片

一、载入与潜伏
画图实际消耗的时间一般并不是影响性能的因素,而且若把图片直接存储在内存,会损耗大量的资源, 因此需要在应用执行的时候周期性地载入和卸载图片。
图片文件的载入速度同一时候受到CPU及IO(输入/输出)延迟的影响。

iOS设备中的闪存已经比传统硬盘快很是多了。但仍然比RAM慢将近200倍左右,这就需要慎重地管理载入。以免延迟。

有时候图片也需要从远程网络链接中下载,这将会比从磁盘载入要消耗不少其它的时间,甚至可能由于链接问题而载入失败(在几秒钟尝试以后)。你不能在主线程中载入网络,并在屏幕冻结期间指望用户去等待它。因此需要后台线程。


二、异步线程载入
由于图片的载入是十分耗时间的,若把该操做放置在主线程中运行会减小CPU的利用率甚至拖慢帧率。可以经过GCD或者NSOperationQueue来实现异步载入最后再在主线程中同步发起渲染就能够。

三、延迟解压
一旦图片文件被载入使用。就必须要通过解码(解压)的过程,解码过程是一个至关复杂的任务。耗时长也占大量的内存。
对于PNG:载入相对长。文件相对更大,但解码比較快。
对于JPEG:载入快,图片小,但解码算法复杂耗时长。

由于iOS系统会让载入完毕的图片不会立刻解压,而是到需要用的时刻才正式解压。因此会影响性能。

  • iOS系统提供下面三种方式来实现绕过延迟解压的机制
    • imageName方法是能避免延迟载入。而且该方法在载入后会立刻解压,但仅仅相应用资源束有效。
    • 将载入图片设置为图层内容。如UIImageView的iamge属性。但这需要在主线程运行。因此不会对性能有大的提高
    • 使用ImageIO框架

四、使用CATiledLayer实现异步载入和显示大型图片
略。


五、分辨率交换
略。


六、使用imageNamed实现缓存
imageName方法是能避免延迟载入,而且该方法在载入后会立刻解压,但仅仅相应用资源束有效。因此网络图片无效。
以前咱们提到使用 [UIImage imageNamed:] 载入图片有个优势在于可以立马解压图片而不用等到绘制的时候。但是 [UIImage imageNamed:] 方法有还有一个很显著的优势:它在内存中本身主动缓存了解压后的图片。即便你本身没有保留对它的不论什么引用。

因此也要注意不能用于载入大图片,否则会占用大量的内存资源。


  • 对于下面场景不能使用imageName。需要本身实现缓存机制:
    • [UIImage imageNamed:]方法只适用于在应用程序资源束文件夹下的图片,但是大多数应用的不少图片都要从网络或者是用户的相机中获取,因此 [UIImage imageNamed:]就无法用了。
    • [UIImage imageNamed:]缓存用来存储应用界面的图片(button,背景等等)。假设对比片这样的大图也用这样的缓存,那么iOS系统就很是可能会移除这些图片来节省内存。

      那么在切换页面时性能就会降低,因为这些图片都需要又一次载入。对传送器的图片使用一个单独的缓存机制就行把它和应用图片的生命周期解耦。

    • [UIImage imageNamed:]缓存机制并不是公开的。因此你不能很是好地控制它。好比,你无法作到检測图片是否在载入以前就作了缓存。不能够设置缓存大小,当图片无用的时候也不能把它从缓存中移除。

七、本身定义缓存

  • 若需要实现本身的缓存机制。一般得从下面四个方面进行考虑:
    • 选择一个合适的缓存键 - 缓存键用来作图片的惟一标识。

      假设实时建立图片,一般不太好生成一个字符串来区分别的图片。

      在咱们的图片传送带样例中就很是easy,咱们可以用图片的文件名称或者表格索引。

    • 提早缓存 - 假设生成和载入数据的代价很是大。你可能想当第一次需要用到的时候再去载入和缓存。

      提早载入的逻辑是应用内在就有的。但是在咱们的样例中,这也很是好实现,因为对于一个给定的位置和滚动方向。咱们就可以精确地推断出哪一张图片将会出现。

    • 缓存失效 - 假设图片文件发生了变化。如何才干通知到缓存更新呢?这是个很是困难的问题(就像菲尔 卡尔顿提到的),但是幸运的是当从程序资源载入静态图片的时候并不需要考虑这些。对用户提供的图片来讲(可能会被改动或者覆盖),一个比較好的方式就是当图片缓存的时候打上一个时间戳以便当文件更新的时候做比較。
    • 缓存回收 - 当内存不够的时候,如何推断哪些缓存需要清空呢?这就需要到你写一个合适的算法了。

      幸运的是,对缓存回收的问题。苹果提供了一个叫作NSCache通用的解决方式



八、NSCache
NSCache和NSDictionary相似。都是直接经过键值进行訪问,但不一样的是,NSCache所持有的对象在内存不足的时候。会本身主动将其释放。
  • 固然开发人员可以经过下面设置来粗颗粒度地进行缓存管理的约束
    • -setCountLimit:方法设置缓存大小
    • -setObject:forKey:cost:来对每个存储的对象指定消耗的值来提供一些暗示
    • -setTotalCostLimit:方法来指定全体缓存的尺寸

九、文件格式与载入性能
略。

Layer Performance(图层性能)


一、隐形绘制
  • 寄宿层可以经过下面方式显示绘制
    • Core Graphics
    • 给contents属性赋值图片
    • 在屏幕外事先绘制CGContext

  • 当发生下面场景会触发隐式绘制
    • 使用特性的图层属性
    • 特定的视图
    • 特定视图的子类

二、文本
CATextLayer与UILable都是直接将文本绘制在图层的寄宿层内,因此要避免频繁的修改,若该文本需要频繁修改,可以先将其放在一个子图层上,经过contentMode来等比例缩放寄宿层。


三、光栅化
咱们提到了 CALayer shouldRasterize 属性(光栅化)。它可以解决重叠透明图层的混合失灵问题。
启用 shouldRasterize 属性会将图层绘制到一个屏幕以外的图像。而后这个图像将会被缓存起来并绘制到实际图层的 contents 和子图层。

假设有很是多的子图层或者有复杂的效果应用,这样作就会比重绘所有事务的所有帧划得来得多。

但是光栅化原始图像需要时间。而且还会消耗额外的内存。

当咱们使用得当时,光栅化可以提供很是大的性能优点(如你在第12章所见),但是必定要避免做用在内容不断变更的图层上,不然它缓存方面的优势就会消失。而且会让性能变的更糟。
为了检測你是否正确地使用了光栅化方式,用Instrument查看一下Color Hits Green和Misses Red项目。是否已光栅化图像被频繁地刷新(这样就说明图层并不是光栅化的好选择,或则你无心间触发了没必要要的改变致使了重绘行为)。
总结:会占用较多内存,要避免反复绘制。因此一旦应用要尽可能下降又一次绘制,而多利用快照缓存。

四、离屏渲染
  • 当图层的下面属性被改动会触发离屏渲染
    • 圆角(当和maskToBounds一块儿使用时)
    • masks(图层蒙板)
    • shadows(阴影)
    • shouldRasterize(光栅化)
    • edge antialiasing(抗锯齿)
    • group opacity(不透明)
    • 渐变

屏幕外渲染和咱们启用光栅化时类似。除了它并无像光栅化图层那么消耗大。子图层并无被影响到,而且结果也没有被缓存,因此不会有长期的内存占用。但是,假设太多图层在屏幕外渲染依旧会影响到性能
对于那些需要动画而且要在屏幕外渲染的图层来讲。你可以用 CAShapeLayer contentsCenter 或者 shadowPath 来得到相同的表现而且较少地影响到性能。

五、混合和过分绘制
开发人员应该尽可能下降重叠图层的反复渲染,因为对于用户看来某些遮挡的图层的内容是可有可无的,但渲染就会消耗资源。因此无论不论什么场景都建议例如如下操做:
  • 给视图的backgroundColor属性设置一个固定的,不透明的颜色
  • 设置opaque属性为YES
  • 明智地使用shouldRasterize属性,可以将一个固定的图层体系折叠成单张图片,这样就不需要每一帧又一次合成了,也就不会有因为子图层之间的混合和过分绘制的性能问题了。
固然光栅化是是详细场景二选择使用,否则也会引入别的性能问题,如占用大量内存。


六、下降图层数量
初始化图层,处理图层,打包经过IPC发给渲染引擎,转化成OpenGL几何图形,这些是一个图层的大体资源开销。其实,一次性能够在屏幕上显示的最大图层数量也是有限的。

确切的限制数量取决于iOS设备。图层类型。图层内容和属性等。

但是总得说来可以容纳上百或上千个,如下咱们将演示即便图层自己并无作什么也会遇到的性能问题。


七、对象回收
处理巨大数量的类似视图或图层时另外一个技巧就是回收他们。对象回收在iOS颇为常见。 UITableView UICollectionView 都实用到, MKMapView 中的动画pin码也实用到,还有其它很是多样例。




Q&A:
  • Q:图层没有寄宿层,是否等于没法显示内容?(寄宿层)
    • A:不是,还有经过矢量图绘制的方式来实现图形呈现。如通用的现在应该就是经过算来来绘制矢量图的,仅仅有需要显示图片或者调用core graphics的时候才会生成寄宿层。因此若非必要不要实现draw方法。否则系统会默认加入一个寄宿层。

  • Q:组透明的点颜色计算公式?(视觉效果)
    • A:若某子view是透明的。那么它的颜色公式为:child_color*alpha + super_color*(1-alpha)。

  • Q:3D效果与共享灭点的详细操做?(变换)
    • A:在作3D效果时要尽可能将所有图形的灭点设置在屏幕的中心。因此建议先在屏幕的中心建立图像。而后经过transform的形式来移动图层。那么就能保证灭点在屏幕中心。
  • Q:3D变化计算?(变换)
    • A:提供了平移、旋转、缩放,但每次操做都是基于二维的,因此注意当前的參考坐标系,剩下的计算与二维同样。
  • Q:立方体的光亮与阴影原理?(变换)
    • A:经过GLK库的函数计算垂直,并加入阴影层。
  • Q:3D点击事件处理?(变换)
    • A:在3D图层中。因为一个点击的点,实际上可能会穿过两个图层的点,这时事件拦截的顺序由subviews数组的顺序认为,也就是谁最靠近用户视觉,谁有限响应。
  • Q:常规的界面绘制是经过矢量绘制仍是寄宿图绘制实现?(专用图层)
    • A:常规界面是矢量绘制完毕的。
  • Q:平面化3d层级结构的意思?(专用图层)
    • A:
  • Q:CATiledLayer解决大图载入(专用图层)
    • A:

0
0
 
 

相关文章
相关标签/搜索