The Layer Tree(图层树)
一、图层与视图的差异:
二、关于视图层级、图层树、呈现树、渲染树
因此以上四层。每一层都负责不一样的任务,其数据流是视图-》图层-》呈现树-》渲染树。
并且绘制周期60FPS也是会随着系统的状态而变化的,若系统资源超载,而会经过下降绘制次数来确保系统能稳定执行。html
三、视图动画
视图动画是显式,因为UIView默认把CALayer的隐式动画禁止掉了。因此要经过动画Block的形式才干取消动画的限制。(动画block实际上就是曾经的动画栈)。
并且视图能实现的动画仅仅是图层所开放的一部分,都是2D动画,因此视图动画的效果会稍稍简单。
四、图层动画
图层动画是隐性动画。仅仅要改动图层的属性。其变化都会以默认的渐变形式呈现出来(详细原理见《隐式动画》)。并且图层动画支持不少其它视图动画没法实现的效果。如:
五、更适宜使用CAlayer呈现内容的场景
因为UIView与CALayer都可以呈现内容。尽管CALayer不能直接实现事件响应。但开发人员也可以经过hit-test机制的原理来本身实现事件响应,那什么场景更加适合用CALayer而不是UIView呢?例如如下所看到的:
|
The Backing Image(寄宿层)
一、寄宿层&Contents属性
CALayer有一个名曰Contents的属性,这个属性是与寄宿层相相应了,而contents属性指向的对象必须为CGImageRef类型(一个指向CGImage结构的指针),因此寄宿层是用来展现图片所用的。例如如下状况会调用寄宿层:
core Graphics可以实现本身定义绘制,但一般不建议那么作,因为core Graphics绘制会默认生成一个绘制专用的存储空间,而这空间有十多M那么多。会占用大量内存,因此Apple不建议实现空的core Graphics更不建议在core Graphics实现不属于该方法的代码。
二、contentGravity属性
与UIView的contentMode属性相相应,用于调整图片的布局,支持一下常量值:
三、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下的三种坐标系统单位:
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提供了两个核心的方法:
五、本身主动布局
对于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提供下面參数来定制阴影的效果:数据库
shadowRadius的设置效果图例如如下:
![]() ![]()
有一点需要注意的,就是阴影的绘制是属于离屏绘制。是比較效果资源的。因此必定要下降同一屏幕进行大量阴影绘制的状况。
四、阴影裁剪
图层的阴影不少其它是继承于内容的外形,而不是依据边界和角半径来肯定。为了计算出阴影的形状。core animation会将寄宿层也考虑在内。
但若直接maskToBounds来裁剪,会把阴影效果也裁减掉。编程
为了解决以上问题。可以经过专门引入一个阴影图层来解决,详细效果例如如下:
图一:直接maskToBounds裁剪会把阴影效果一并裁剪掉。
![]() ![]()
图二:经过加入一个另外的阴影图层在如下,然它来实现阴影。而详细的内容图层就直接maskToBounds裁剪。
![]() ![]()
图三:终于既能实现裁剪,又能实现阴影。
![]() ![]()
PS:无论是阴影仍是圆角裁剪都会引起离屏渲染,大量的该类型的图形需要渲染的话。会减低帧率。
五、shadowPath 属性
阴影并必定是方形的。咱们也可以经过shadowPath来定制本身想要的阴影形状。详细效果例如如下所看到的:
![]() ![]()
六、图层蒙版
经过masksToBounds属性。咱们可以实现边界裁剪,但咱们还需要更加灵活的裁剪需求的时候就可以经过图层蒙版来实现。CALayer提供一个mask的属性来解决一个问题。而mask原本就是指向还有一个图层的指针。其详细原理例如如下图所看到的:
![]() ![]() ![]() ![]()
从上图效果可以知道mask舒心仅仅关心形状的交集,而颜色仍是由原来的图形的颜色决定。
另外一点需要注意的是图层蒙版也会引起离屏渲染,会带来性能问题,因此使用的时候也得多加注意。
七、拉伸过滤
当咱们视图显示一个图片的时候。都应该以正确的比例,正确的1:1像素显示在屏幕上。缘由例如如下:
但有时候咱们就需要缩略图。所专门另外存储缩略图这对内存来讲。
这时就可以经过CALayer的minificationFilter(缩小滤波器)和magnificationFilter(方法滤波器)属性实现图形的缩放算法。当中提供下面三种默认的缩放算法:数组
对于大图的缩放,採用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
|
Implicit Animations(隐式动画)
一、隐式动画&事务
Core Animation基于一个若是,就是屏幕上的所有图形都可以作动画,而且这样的动画不需要开发人员去设置,会本身主动实现。这也是为何直接改动CALayer的属性会以一种渐变的形式来更新到一个新的值。
这样的仅仅需要改动图层的数值就会引起动画的形式来更新到新数值的动画称为隐式动画。
而这样的定义了动画的详细呈现效果的机制,就称为事务。
由于CALayer是默认开启了隐式动画的。而若咱们在CALayer上调用显式动画的话,就考虑到隐式动画对效果的影响,进而决定是否需要取消隐式动画。
二、动画Block
UIView封装了CALayer。但UIView禁止了隐性动画,因此开发人员需要在UIView上实现显性动画。
在iOS4.0以前,UIView採用动画栈来管理显示。后来提供了动画Block这个更加便捷的方式。但原理都是同样的。性能优化
三、图层行为
咱们把改变CALayer属性时本身主动应用的动态称为行为。一组行为的运行步骤例如如下所看到的:
通过以上的整个流程,要么actionForKey返回nil则无动画效果,要么就是返回CAAction,而后CALayer利用它实现动画。
所以可以推进出UIView是怎样禁止隐式动画的,就是讲CALayer的delegate设置为自身。而后在actionForLayer:forKey方法中返回nil。
四、呈现与模型
在iOS中。屏幕每秒钟又一次刷新屏幕60次。
因此Core Animaiton就需要在设置一次新值和新值生效之间,对屏幕上的图层进行又一次组织。微信
正是觉得上面的机制的存在。因此才会有iOS才会有图层以及呈现层之间的关系,图层更像是model,而呈现层就是view,Core Animation就是Controller,因此在一个绘制周期内,图层负责收集与保存用户对属性的改动,到绘制时刻时。就将图层的数据覆盖到呈现树。
咱们可以经过CALayer的 -presentationLayer方法来获取正在屏幕上显示的呈现树的数据,尽管通常不需要。但在下面状况下会比較有做用
|
Explicit Animations(显式动画)
一、Core Animation的类图架构
![]() ![]()
二、基础属性动画
基础动画主要由CABasicAnimation实现,由上图可知道。而CABasicAnimation继承于CAPropertyAnimation属性动画,经过设置下面三个数值以及其它的选项,就能够实现本身主动动画:
当中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协议下定义的一些核心属性:
可以经过将repeatCount或者repeatDuration设置为INFINITY来实现动画无限循环播放。但不能同一时候使用这两个属性。
二、相对时间
在Core Animation中,时间是相对的,每个动画都有本身的描写叙述时间,可以独立的加速、延迟或者偏移,当中有下面关键属性:
三、fillMode
当一个图层产生动画,实际上就是呈现层在运行动画。那么。当呈现层运行完动画是否要讲当前属性的值覆盖会图层。这样的行为就称为fill mode,这个由开发人员决定:
四、全局时间与本地时间
对
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
三、缓冲和关键帧动画
CAKeyframeAnimation
有一个
NSArray
类型的
timingFunctions
属性。咱们可以用它来对每次动画的步骤指定不一样的计时函数。但是指定函数的个数必定要等于
keyframes
数组的元素个数
减一
,因为它是描写叙述每一帧之间动画速度的函数。
四、本身定义缓冲函数
临时不讨论。
|
Timer-Based Animation(基于定时器的动画)——咱们可以经过事务来实现动画。设置关键帧,让中间的过渡本身主动计算得出。也可以基于定时器来设置每一个绘制周期的变换来实现动画。
iOS提供一下两种形式来实现基于定时器的动画:
但无论是NSTimer仍是CADispaly本质上都是基于runloop,NSTImer把每个时间触发的事件注冊到runloop里面。以待制定。
而CADisplay则把任务直接嵌套进绘制操做以前。 |
Tuning for Speed(性能调优)
一、CPU & GPU
CPU(中央处理器)和GPU(图形处理器)都是能用来处理。
总的来讲。咱们可以用软件(使用CPU)作不论什么事情,但是对于图像处理,通常用硬件会更快,因为GPU使用图像对高度并行浮点运算作了优化。
因为某些缘由,咱们想尽量把屏幕渲染的工做交给硬件去处理。问题在于GPU并无无限制处理性能。而且一旦资源用完的话,性能就会開始降低了(即便CPU并无全然占用)。
性能优化的本质就会合理地利用CPU与GPU,使他们不会超出负荷。
Core Animation处于iOS的核心地位,无论是应用内仍是应用外都会用到它。因此iOS特别设计了一个进程来运行渲染相关的任务,也叫渲染服务。渲染服务管理动画和屏幕上组合的图层。
当执行一段动画的时候。这个过程会被切分为六个阶段,包含应用内的四个阶段,与应用外的2个阶段。当中应用内的阶段例如如下:
当数据被打包到渲染服务进程。会将其反序列化造成还有一个渲染树。
使用这个渲染树对动画的每一帧作出例如如下工做:
减小GPU图层绘制的部分场景:
减小CPU图层绘制的部分场景:
二、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提供了一下两种方案来实现异步绘制,提升界面的响应效率:
|
Image IO(图像IO)——研究怎样优化从闪存或者网络中载入和显示图片
一、载入与潜伏
画图实际消耗的时间一般并不是影响性能的因素,而且若把图片直接存储在内存,会损耗大量的资源,
因此需要在应用执行的时候周期性地载入和卸载图片。
图片文件的载入速度同一时候受到CPU及IO(输入/输出)延迟的影响。
iOS设备中的闪存已经比传统硬盘快很是多了。但仍然比RAM慢将近200倍左右,这就需要慎重地管理载入。以免延迟。
有时候图片也需要从远程网络链接中下载,这将会比从磁盘载入要消耗不少其它的时间,甚至可能由于链接问题而载入失败(在几秒钟尝试以后)。你不能在主线程中载入网络,并在屏幕冻结期间指望用户去等待它。因此需要后台线程。
二、异步线程载入
由于图片的载入是十分耗时间的,若把该操做放置在主线程中运行会减小CPU的利用率甚至拖慢帧率。可以经过GCD或者NSOperationQueue来实现异步载入最后再在主线程中同步发起渲染就能够。
三、延迟解压
一旦图片文件被载入使用。就必须要通过解码(解压)的过程,解码过程是一个至关复杂的任务。耗时长也占大量的内存。
对于PNG:载入相对长。文件相对更大,但解码比較快。
对于JPEG:载入快,图片小,但解码算法复杂耗时长。
由于iOS系统会让载入完毕的图片不会立刻解压,而是到需要用的时刻才正式解压。因此会影响性能。
四、使用CATiledLayer实现异步载入和显示大型图片
略。
五、分辨率交换
略。
六、使用imageNamed实现缓存
imageName方法是能避免延迟载入,而且该方法在载入后会立刻解压,但仅仅相应用资源束有效。因此网络图片无效。
以前咱们提到使用
[UIImage imageNamed:]
载入图片有个优势在于可以立马解压图片而不用等到绘制的时候。但是
[UIImage imageNamed:]
方法有还有一个很显著的优势:它在内存中本身主动缓存了解压后的图片。即便你本身没有保留对它的不论什么引用。因此也要注意不能用于载入大图片,否则会占用大量的内存资源。
七、本身定义缓存
八、NSCache
NSCache和NSDictionary相似。都是直接经过键值进行訪问,但不一样的是,NSCache所持有的对象在内存不足的时候。会本身主动将其释放。
九、文件格式与载入性能
略。
|
Layer Performance(图层性能)
一、隐形绘制
二、文本
CATextLayer与UILable都是直接将文本绘制在图层的寄宿层内,因此要避免频繁的修改,若该文本需要频繁修改,可以先将其放在一个子图层上,经过contentMode来等比例缩放寄宿层。
三、光栅化
咱们提到了
CALayer
的
shouldRasterize
属性(光栅化)。它可以解决重叠透明图层的混合失灵问题。
启用
shouldRasterize
属性会将图层绘制到一个屏幕以外的图像。而后这个图像将会被缓存起来并绘制到实际图层的
contents
和子图层。假设有很是多的子图层或者有复杂的效果应用,这样作就会比重绘所有事务的所有帧划得来得多。 但是光栅化原始图像需要时间。而且还会消耗额外的内存。
当咱们使用得当时,光栅化可以提供很是大的性能优点(如你在第12章所见),但是必定要避免做用在内容不断变更的图层上,不然它缓存方面的优势就会消失。而且会让性能变的更糟。
为了检測你是否正确地使用了光栅化方式,用Instrument查看一下Color Hits Green和Misses Red项目。是否已光栅化图像被频繁地刷新(这样就说明图层并不是光栅化的好选择,或则你无心间触发了没必要要的改变致使了重绘行为)。
总结:会占用较多内存,要避免反复绘制。因此一旦应用要尽可能下降又一次绘制,而多利用快照缓存。
四、离屏渲染
屏幕外渲染和咱们启用光栅化时类似。除了它并无像光栅化图层那么消耗大。子图层并无被影响到,而且结果也没有被缓存,因此不会有长期的内存占用。但是,假设太多图层在屏幕外渲染依旧会影响到性能
对于那些需要动画而且要在屏幕外渲染的图层来讲。你可以用
CAShapeLayer
,
contentsCenter
或者
shadowPath
来得到相同的表现而且较少地影响到性能。
五、混合和过分绘制
开发人员应该尽可能下降重叠图层的反复渲染,因为对于用户看来某些遮挡的图层的内容是可有可无的,但渲染就会消耗资源。因此无论不论什么场景都建议例如如下操做:
固然光栅化是是详细场景二选择使用,否则也会引入别的性能问题,如占用大量内存。
六、下降图层数量
初始化图层,处理图层,打包经过IPC发给渲染引擎,转化成OpenGL几何图形,这些是一个图层的大体资源开销。其实,一次性能够在屏幕上显示的最大图层数量也是有限的。
确切的限制数量取决于iOS设备。图层类型。图层内容和属性等。
但是总得说来可以容纳上百或上千个,如下咱们将演示即便图层自己并无作什么也会遇到的性能问题。
七、对象回收
处理巨大数量的类似视图或图层时另外一个技巧就是回收他们。对象回收在iOS颇为常见。
UITableView
和
UICollectionView
都实用到,
MKMapView
中的动画pin码也实用到,还有其它很是多样例。
|