- The Layer Beneath
-
- The Layer Tree(图层树)
- The Backing Image(寄宿层)
- Layer Geometry(图层几何学)
- Visual Effects(视觉效果)
- Transforms(变换)
- Specialized Layers(专有图层)
- Setting Things in Motion
-
- Implicit Animations(隐式动画)
- Explicit Animations(显式动画)
- Layer Time(图层时间)
- Easing(缓冲)
- Timer-Based Animation(基于定时器的动画)
- The Performance of a Lifetime
-
- Tuning for Speed(性能调优)
- Efficient Drawing(高效画图)
- Image IO(图像IO)
- Layer Performance(图层性能)
The Layer Tree(图层树)
1、图层与视图的差别:
2、关于视图层级、图层树、呈现树、渲染树
所以以上四层。每一层都负责不同的任务,其数据流是视图-》图层-》呈现树-》渲染树。
而且绘制周期60FPS也是会随着系统的状态而变化的,若系统资源超载,而会通过降低绘制次数来确保系统能稳定执行。 3、视图动画
视图动画是显式,由于UIView默认把CALayer的隐式动画禁止掉了。所以要通过动画Block的形式才干取消动画的限制。(动画block实际上就是曾经的动画栈)。
而且视图能实现的动画仅仅是图层所开放的一部分,都是2D动画,所以视图动画的效果会稍稍简单。
4、图层动画
图层动画是隐性动画。仅仅要改动图层的属性。其变化都会以默认的渐变形式呈现出来(详细原理见《隐式动画》)。而且图层动画支持很多其它视图动画无法实现的效果。如:
5、更适宜使用CAlayer呈现内容的场景
由于UIView与CALayer都能够呈现内容。尽管CALayer不能直接实现事件响应。但开发人员也能够通过hit-test机制的原理来自己实现事件响应,那什么场景更加适合用CALayer而不是UIView呢?例如以下所看到的:
|
The Backing Image(寄宿层)
1、寄宿层&Contents属性
CALayer有一个名曰Contents的属性,这个属性是与寄宿层相相应了,而contents属性指向的对象必须为CGImageRef类型(一个指向CGImage结构的指针),所以寄宿层是用来展示图片所用的。例如以下情况会调用寄宿层:
core Graphics能够实现自己定义绘制,但通常不建议那么做,由于core Graphics绘制会默认生成一个绘制专用的存储空间,而这空间有十多M那么多。会占用大量内存,所以Apple不建议实现空的core Graphics更不建议在core Graphics实现不属于该方法的代码。
2、contentGravity属性
与UIView的contentMode属性相相应,用于调整图片的布局,支持一下常量值:
3、contentsScale
contentsScale属性定义了寄宿层的像素尺寸和视图大小比例,默认是1.0(一个点一个像素),在retina屏幕得设置在2.0(一个点两个像素)。在plus设备上得设备为3.0(一个点3个像素)。
但若contentGravity设置了如kCAGravityResizeAspectFill自己主动适配大小的属性后,contentsScale会不起作用。所以不能依靠contentsScale来做缩放的操作,缩放还是得交给transform或者affineTransform。
3、maskToBounds
该属性与UIView的clipsToBounds属性相相应。都是应用于将超出图层边界的子图层的内容进行裁剪。
这里须要一点须要注意的,但我们实现radiusCorner时,实际上就是通过设置背景颜色来实现的。而与maskToBounds结合才是真正把边角内容裁剪掉,但radiusCorner+maskToBounds结合使用会引发离屏渲染。所以在radiusCorner满足要求。就不要再调用maskToBounds。
4、contentsRect
CALayer的contentsRect同意我们在图层边框内显示寄宿图的一个子域,而contentsRect是一个相对值属性,如{0,0。1,1}表示整个寄宿图,这样的坐标系统也叫单位。例如以下简述iOS下的三种坐标系统单位:
contentsRect有一种经典的使用方法——image sprites(图片拼接,经常使用于游戏),这种使用方法就是用来实现一次性载入多张图片的。由于每一张图片的使用前都要经过,载入——》压缩——》呈现,的一个过程当中载入和压缩是十分耗时的,载入消耗IO,压缩算法的复杂会添加CPU的消耗,所以想游戏这种App须要载入解压大量的图片时,能够把全部图片整合成一张图片来载入解压,然后通过contentsRect裁剪出子图,并显示,能够优化载入解压的过程。
5、contentsCenter
contentsCenter的名字事实上有一点的误导性,事实上际上与UIView的Stretching属性相相应。用来实现寄宿层的缩放时的呈现效果。也是一个相对值单位。
6、customs drwaing
除了通过CGImage设置到contents的方式来实现寄宿层。还能够通过Core Graphics来直接绘制寄宿层,但这样的方式十分消耗内存不建议使用。
CALayer有一个可选的Delegate属性。实现了CALayerDelegate,但CALayer须要又一次绘制,则调用它的displayLayer方法(与UIView的setNeedDispaly相相应),就会调用其代理方法drawLayer:inContext(与UIView的drawRect相相应)。
若在视图层中,仅仅要用户实现了drawRect那么仅仅要屏幕须要又一次绘制那么该方法都会被默认调用。 |
Layer Geometry(图层几何学)
1、布局
UIView的三个重要布局属性:frame、bounds、center
CALayer的三个重要的布局属性:frame、bounds、position
能够看出UIView与CALayer的布局属性是一一相应的,而唯一有啥区别的就是锚点的命名,UIView为center,而CALayer为position,但锚点最開始都是默觉得图形的中心。
当中frame实际上就是一个虚拟属性,其由bounds与center/position计算所得。所以改动bounds与center/position也会影响到frame的数值。
还有不管是视图还是图层。在屏幕上不管怎么变形,本质上其还是一个矩形。但在旋转的情况下。frame的数值是会事实变动的,例如以下图:
2、锚点
锚点属性是用来指定图层相对于父图层的位置。也能够理解为利用图层的句柄。默觉得图层的中心但也能够改动。在图层中改动了锚点会产生移动(隐性动画),相同的锚点也是一个相对值。
例如以下。为锚点的实验效果图:
图一:时分秒针直接以图层的中点为锚点,由于旋转时也以其为中心,效果例如以下:
图二:将锚点改动成指针的尾端
图三:改动锚点后的旋转效果。
3、坐标系
由于每一个图层都有自己的坐标系,所以CALayer也提供了一系列的方法用于一个点在不同的坐标系之间转换,这些方法特别适合于子图层与父图层之间的坐标转换。
事实上UIView与CALayer都有zPosition。但因为UIView只支持2D变换,所以UIView的zPosition只适合于用来作图层的调整,但更加建议用UIView提供的视图数组管理方法来调整视图的绘制顺序来调整。而CALayer的zPosition是一个重要的三维坐标系。
4、Hit Testing
Hit Testing是iOS中一个十分重要的机制,用于检索点击了的目标图标,与响应链条相互搭配的话。就会将终于目标图标设置为第一响应者。Hit Testing提供了两个核心的方法:
5、自己主动布局
对于UIView。通过UIView暴露出来的UIViewAutoresizingMask和NSLayoutConstraint来实现自己主动布局。
但对于CALayer,则须要手动来操作实现,当中最为简单的方法就是实现CALayerDelegate的例如以下函数:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
在该方法内依据当前layer的属性来计算调整来实现自己主动布局。
由于CALayer实现自己主动布局不方便,所以这也是为什么更加建议使用UIView来实现界面的构建。
|
Visual Effects(视觉效果)——本节探讨可以通过CALayer实现的附加视觉效果
1、圆角
CALayer有一个叫做cornerRadius的属性控制图层角的曲率,这个曲率仅仅影响背景颜色而不影响图片或者子图片。圆角的计算原理就是一个以CALayer的中点,曲率所设的半径的原与矩形的交集就是目标图形。
另一个名曰maskToBounds属性,就是实现将图层里面边界外的图形所有分割丢弃。
所以通过cornerRadius+maskToBounds组合能够实现圆角,但同一时候会引发离屏渲染,若是小量的离屏渲染还好,但如滚动栏的场景,开会有大量的离屏渲染的任务产生,就会严重影响性能,这一点也是注意优化的。详细效果例如以下图所看到的:
2、图层边框
通过设置CALayer的borderWidth与borderColor两个属性能够改动边框的效果。
效果例如以下: 3、阴影
阴影是一种能达到图层深度暗示效果的装饰。
CALayer提供下面參数来定制阴影的效果:
shadowRadius的设置效果图例如以下:
有一点须要注意的,就是阴影的绘制是属于离屏绘制。是比較效果资源的。所以一定要降低同一屏幕进行大量阴影绘制的情况。
4、阴影裁剪
图层的阴影很多其它是继承于内容的外形,而不是依据边界和角半径来确定。为了计算出阴影的形状。core animation会将寄宿层也考虑在内。
但若直接maskToBounds来裁剪,会把阴影效果也裁减掉。 为了解决以上问题。能够通过专门引入一个阴影图层来解决,详细效果例如以下:
图一:直接maskToBounds裁剪会把阴影效果一并裁剪掉。
图二:通过加入一个另外的阴影图层在以下,然它来实现阴影。而详细的内容图层就直接maskToBounds裁剪。
图三:终于既能实现裁剪,又能实现阴影。
PS:不管是阴影还是圆角裁剪都会引发离屏渲染,大量的该类型的图形须要渲染的话。会减低帧率。
5、shadowPath 属性
阴影并一定是方形的。我们也能够通过shadowPath来定制自己想要的阴影形状。详细效果例如以下所看到的:
6、图层蒙版
通过masksToBounds属性。我们能够实现边界裁剪,但我们还须要更加灵活的裁剪需求的时候就能够通过图层蒙版来实现。CALayer提供一个mask的属性来解决一个问题。而mask本来就是指向还有一个图层的指针。其详细原理例如以下图所看到的:
从上图效果能够知道mask舒心仅仅关心形状的交集,而颜色还是由原来的图形的颜色决定。
另一点须要注意的是图层蒙版也会引发离屏渲染,会带来性能问题,所以使用的时候也得多加注意。
7、拉伸过滤
当我们视图显示一个图片的时候。都应该以正确的比例,正确的1:1像素显示在屏幕上。原因例如以下:
但有时候我们就须要缩略图。所专门另外存储缩略图这对内存来说。
这时就能够通过CALayer的minificationFilter(缩小滤波器)和magnificationFilter(方法滤波器)属性实现图形的缩放算法。当中提供下面三种默认的缩放算法:
对于大图的缩放,採用kCAFilterLinear、kCAFilterTrilinear,效果会比較好。
对于小图、较少斜线的图的缩放,。採用kCAFilterNearest效果比較好。
图一:採用kCAFilterLinear的方法滤波器算法的效果。
图二:採用kCAFilterNearest的放大滤波器算法的效果:
8、透明组
UIView有一个alpha属性类确定视图的透明度。而CALayer有一个与之相应的属性——opacity。这两个属性都会影响到子图层。例如以下图所看到的:
当我们将view2的alpha数值设置为0.5,这时候,对于label所处的点的颜色计算公式为:50%label + 25%view + 25%background,所以就是灰色偏白的颜色。
若想整个图层的色调保持一致,能够通过将shouldRasterize属性设置为YES,那么图层及其子图层将被整合成一个总体的图片。效果例如以下:
图一:shouldRasterize属性设置为默认值NO的效果。
图二:shouldRasterize属性设置为默认值YES的效果。
|
Transforms(变换)
1、仿射变换
UIView相应的transfrom属性是一个CGAffineTransfrom类型。用于实现二维空间旋转、平移、缩放、斜切。
而CALayer的transform属性是一个CATransforms3D。能应用3维空间进行旋转、平移、缩放、斜切等效果。
当中,仿射变化的数学原理例如以下:
只是iOS为了运算的方便。已经提供了三个标准方法分别用于实现旋转、缩放。平移,而斜切得靠原生运算来实现。
当中有一点须要注意的,就是旋转的angle是以弧度制为单位的。
iOS系统提供下面方法来实现混合变换,能同一下面的组合函数实现复杂的变形模式:
图一:创建一个空的变换结构体。
图二:不断叠加变换结构体
图三:直接将两个变换结构体合并
剪切变换的效果与事实上现代码:
2、3D变化
3D变化与2D变化是十分相似的,不同的仅仅是3D变换是3个维度的,加入了Z轴。其数学原理例如以下:
这样的矩阵运算还是挺麻烦的。所以iOS系统提供给我们便捷的方法:
相同也会有变换叠加的方法,当中旋转的时候一定要注意当前的旋转的參考轴,顺时针旋转为正值,逆时针旋转为负值。
3、透视投影
在真实的世界中,当物体远离我们。因为视角的原因,其会变小。所以为了让3D效果更佳真实,须要引入投影变换,尽管core animation并没有提供给我们,但十分简单,其数学实现例如以下:
仅仅要将m34 = -1/d, d= 500~1000,(d越小越失真,d越大透视效果越弱)。
图3.1:没实现透视投影的旋转效果。
图3.2:实现了透视投影的旋转效果。
4、灭点
在人的视觉中。当物体离人越远则会变得越小,当接近无穷远的时候就会汇聚成一个不可见的点,这个点就叫灭点。现实生活中,这个点通常就是中心点,所以在程序中我们也要让图形的灭点集中在屏幕的中点,例如以下所看到的:
而实现方式则是。首先加入图形的时候先以屏幕中点为锚点加入,然后通过transfrom来讲图层移动到恰当的位置。
5、sublayerTransform
sublayerTransform是一种将全部对应的配置统一设置到该图层的全部子图层的便捷属性。
6、3D坐标下的图层背面
在3D坐标下,图层的背面也是会绘制的,并且是正面的镜像。
我们能够通过CALayer的doubleSided属性来决定是否运行双面绘制。
7、扁平化图层
假设父图层的坐标发生变换,那么子图层也会随着父图层而进行相同的变换,如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图形都共享一个标准坐标系。
8、3D场景下的光亮和阴影
略。临时不懂。
9、3D场景下的点击事件
在处理点击事件,一定要注意点击响应是由UIView中的图层结构来决定的,而不是CALayer在屏幕上的呈现效果,所以得注意事件拦截的顺序。
能够通过设置userInteractionEnabled以及简单的视图覆盖来实现点击事件正确的传递。
|
Specialized Layers(专有图层)
1、CAShapeLayer
2、CATextLayer
3、CATransformLayer
4、CAGradientLayer
5、CAReplicatorLayer
6、CAScrollLayer
7、CATiledLayer
8、CAEmitterLayer
9、CAEAGLlayer
10、AVPlayerLayer
|
Implicit Animations(隐式动画)
1、隐式动画&事务
Core Animation基于一个如果,就是屏幕上的全部图形都能够做动画,并且这样的动画不须要开发人员去设置,会自己主动实现。这也是为什么直接改动CALayer的属性会以一种渐变的形式来更新到一个新的值。
这样的仅仅须要改动图层的数值就会引发动画的形式来更新到新数值的动画称为隐式动画。
而这样的定义了动画的详细呈现效果的机制,就称为事务。
因为CALayer是默认开启了隐式动画的。而若我们在CALayer上调用显式动画的话,就考虑到隐式动画对效果的影响,进而决定是否须要取消隐式动画。
2、动画Block
UIView封装了CALayer。但UIView禁止了隐性动画,所以开发人员须要在UIView上实现显性动画。
在iOS4.0之前,UIView採用动画栈来管理显示。后来提供了动画Block这个更加便捷的方式。但原理都是一样的。 3、图层行为
我们把改变CALayer属性时自己主动应用的动态称为行为。一组行为的运行步骤例如以下所看到的:
经过以上的整个流程,要么actionForKey返回nil则无动画效果,要么就是返回CAAction,然后CALayer利用它实现动画。
因此能够推动出UIView是怎样禁止隐式动画的,就是讲CALayer的delegate设置为自身。然后在actionForLayer:forKey方法中返回nil。
4、呈现与模型
在iOS中。屏幕每秒钟又一次刷新屏幕60次。
所以Core Animaiton就须要在设置一次新值和新值生效之间,对屏幕上的图层进行又一次组织。 正是以为上面的机制的存在。所以才会有iOS才会有图层以及呈现层之间的关系,图层更像是model,而呈现层就是view,Core Animation就是Controller,所以在一个绘制周期内,图层负责收集与保存用户对属性的改动,到绘制时刻时。就将图层的数据覆盖到呈现树。
我们能够通过CALayer的 -presentationLayer方法来获取正在屏幕上显示的呈现树的数据,尽管一般不须要。但在下面情况下会比較有作用
|
Explicit Animations(显式动画)
1、Core Animation的类图架构
2、基础属性动画
基础动画主要由CABasicAnimation实现,由上图可知道。而CABasicAnimation继承于CAPropertyAnimation属性动画,通过设置下面三个数值以及其它的选项,就可以实现自己主动动画:
当中toValue与byValue不能同一时候使用,toVaule是绝对值,byValue是相对值。
属性动画都是针对关键帧的动画。也就是说仅仅关心出发点与结束点,中间的过渡能够自己生成也能够默认。 3、关键帧动画
CAKeyFrameAnimation用于实现关键帧动画,能通过设置其animations数组来定义每一个关键帧的位置,也能够直接通过path属性来定义运动的路径。
4、affineTransform属性
若我们让一个图形沿着曲线运动。能够通过设置affineTransform。让其能沿着曲线的切线运动从而让运动更加真实。前后效果图例如以下图所看到的:
5、虚拟属性
属性动画实际上是针对关键路径而不是一个键的。这就意味着能够对子属性甚至虚拟属性做动画。
如若我们想实现旋转的效果,本来我们须要在keyPath上设置“transform”,
若使用虚拟属性。能够直接在keyPath上直接设置“transfrom.rotation”,这样在设置formValue与toValue时就能够直接设置弧度叫的值。
事实上transfrom.rotation这个属性是并不存在的,而是core aniamiton自己主动依据CAValueFunction来又一次计算transform的数值所得,正由于如此才称之为虚拟属性。
6、动画组
CAAnimationGroup是一种组合动画的解决方式。通过CAAnimationGroup加入沿曲线运动与颜色变化的动画。
7、过渡
略。
8、在动画的过程中取消动画
Core Animation提供了一部分的方法来实现动画加入以及移除。例如以下图所看到的:
- (CAAnimation *)animationForKey:(NSString *)key; //实现动画的加入,当中key不仅能够用来訪问动画,还能够用来移除动画 - (void)removeAnimationForKey:(NSString *)key;
//移除Key指定的动画 - (void)removeAllAnimations;
//移动CALayer已经加入的全部动画 |
Layer Time(图层时间)
1、CAMediaTiming协议
CAMediaTiming协议定义了在一段动画内控制逝去时间的属性集合。
CALayer和CAAnimaition都实现了该协议,所以时间能够被随意图层或者一段动画的类所控制。 CAMediaTiming协议下定义的一些核心属性:
能够通过将repeatCount或者repeatDuration设置为INFINITY来实现动画无限循环播放。但不能同一时候使用这两个属性。
2、相对时间
在Core Animation中,时间是相对的,每一个动画都有自己的描写叙述时间,能够独立的加速、延迟或者偏移,当中有下面关键属性:
3、fillMode
当一个图层产生动画,实际上就是呈现层在运行动画。那么。当呈现层运行完动画是否要讲当前属性的值覆盖会图层。这样的行为就称为fill mode,这个由开发人员决定:
4、全局时间与本地时间
对
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的动画。
5、暂停、倒回和快进的实现
设置动画的
speed 属性为0能够暂停动画。但不能再加入后再改动。否则会报错。但我们能够通过下面的设置来实现一些调试:
self.window.layer.speed = 100; 6、手动动画
将speed设置为0让动画停止播放。然后改动timeOffset来切换不时间的动画序列。从而实现手动切花动画的效果。
|
Easing(缓冲)——让动画更加平滑自然
1、CALayer、Animation的缓冲动画与CAMediaTimingFucntion
首先须要设置
CAAnimation 的timingFunction 属性,是CAMediaTimingFunction 类的一个对象。假设想改变隐式动画的计时函数,相同也能够使用CATransaction 的+setAnimationTimingFunction: 方法。来实现缓冲函数。当中有下面默认的缓冲函数:kCAMediaTimingFunctionLinear //线性
kCAMediaTimingFunctionEaseIn //缓进
kCAMediaTimingFunctionEaseOut //缓出
kCAMediaTimingFunctionEaseInEaseOut //缓进缓出
kCAMediaTimingFunctionDefault //默认的效果与 kCAMediaTimingFunctionEaseInEaseOut类似,但效果更佳不明显2、UIView的缓冲动画
通过设置UIView的options參数加入例如以下常量也能够实现缓冲动画的效果:
UIViewAnimationOptionCurveEaseInOut 3、缓冲和关键帧动画
CAKeyframeAnimation 有一个NSArray 类型的timingFunctions 属性。我们能够用它来对每次动画的步骤指定不同的计时函数。可是指定函数的个数一定要等于keyframes 数组的元素个数减一,由于它是描写叙述每一帧之间动画速度的函数。4、自己定义缓冲函数
临时不讨论。
|
Timer-Based Animation(基于定时器的动画)——我们能够通过事务来实现动画。设置关键帧,让中间的过渡自己主动计算得出。也能够基于定时器来设置每个绘制周期的变换来实现动画。
iOS提供一下两种形式来实现基于定时器的动画:
但不管是NSTimer还是CADispaly本质上都是基于runloop,NSTImer把每一个时间触发的事件注冊到runloop里面。以待制定。
而CADisplay则把任务直接嵌套进绘制操作之前。 |
Tuning for Speed(性能调优)
1、CPU & GPU
CPU(中央处理器)和GPU(图形处理器)都是能用来处理。
总的来说。我们能够用软件(使用CPU)做不论什么事情,可是对于图像处理,通经常使用硬件会更快,由于GPU使用图像对高度并行浮点运算做了优化。
由于某些原因,我们想尽可能把屏幕渲染的工作交给硬件去处理。问题在于GPU并没有无限制处理性能。并且一旦资源用完的话,性能就会開始下降了(即使CPU并没有全然占用)。 性能优化的本质就会合理地利用CPU与GPU,使他们不会超出负荷。
Core Animation处于iOS的核心地位,不管是应用内还是应用外都会用到它。所以iOS特别设计了一个进程来运行渲染相关的任务,也叫渲染服务。渲染服务管理动画和屏幕上组合的图层。
当执行一段动画的时候。这个过程会被切分为六个阶段,包含应用内的四个阶段,与应用外的2个阶段。当中应用内的阶段例如以下:
当数据被打包到渲染服务进程。会将其反序列化形成还有一个渲染树。
使用这个渲染树对动画的每一帧做出例如以下工作:
减少GPU图层绘制的部分场景:
减少CPU图层绘制的部分场景:
2、Instruments实现App性能优化,操作顺序通常例如以下:
|
Efficient Drawing(高效画图)
1、软件绘制
软件画图意为不借助GPU的图形绘制。在iOS中通常就是由Core Graphics来实现。但其对照Core Animation和OpenGL,Core Graphics要慢不少,并且也十分消耗内存。
软件画图不仅效率低,还会消耗可观的内存。
CALayer 仅仅须要一些与自己相关的内存:仅仅有它的寄宿图会消耗一定的内存空间。即使直接赋给contents 属性一张图片,也不须要添加额外的照片存储大小。假设同样的一张图片被多个图层作为contents 属性,那么他们将会共用同一块内存,而不是复制内存块。可是一旦你实现了
CALayerDelegate 协议中的-drawLayer:inContext: 方法或者UIView 中的-drawRect: 方法(事实上就是前者的包装方法),图层就创建了一个绘制上下文,这个上下文须要的大小的内存可从这个算式得出:图层宽*图层高*4字节。宽高的单位均为像素。对于一个在Retina
iPad上的全屏图层来说。这个内存量就是 2048*1526*4字节,相当于12MB内存,图层每次重绘的时候都须要又一次抹掉内存然后又一次分配。软件画图的代价昂贵,除非绝对必要。你应该避免重绘你的视图。
提高绘制性能的秘诀就在于尽量避免去绘制。 2、矢量图形
我们用Core Graphics来画图的一个通常原因就是仅仅是用图片或是图层效果不能轻易地绘制出矢量图形。矢量画图包括一下这些:
文章中实现了例如以下效果的demo:
事实上现思路就是每当有touch event发生,则向UIBezierPath中加入一条直线。但在对于屏幕中每当有一个touch event事件发生,意味着整个屏幕都要又一次绘制一遍,当须要又一次绘制的内容越来越多,则会引起帧率的下降。
这时候我们能够通过脏矩阵来对这个问题进行优化。 3、脏矩形
脏矩形是一个能制定又一次绘制区域的机制。
在程序中则是通过用setNeedDispalyInRect来替代setNeedDispaly方法来实现绘制详细的区域,详细实现见文章。 4、异步绘制
UIKit的单线程天性意味着寄宿图通常要在主线程上更新,这意味着绘制会打断用户交互,甚至让整个app看起来处于无响应状态。我们对此无能为力,可是假设能避免用户等待绘制完毕就好多了。
针对这个问题,core Animation提供了一下两种方案来实现异步绘制,提高界面的响应效率:
|
Image IO(图像IO)——研究怎样优化从闪存或者网络中载入和显示图片
1、载入与潜伏
画图实际消耗的时间通常并非影响性能的因素,并且若把图片直接存储在内存,会损耗大量的资源,所以须要在应用执行的时候周期性地载入和卸载图片。
图片文件的载入速度同一时候受到CPU及IO(输入/输出)延迟的影响。
iOS设备中的闪存已经比传统硬盘快非常多了。但仍然比RAM慢将近200倍左右,这就须要慎重地管理载入。以避免延迟。 有时候图片也须要从远程网络连接中下载,这将会比从磁盘载入要消耗很多其它的时间,甚至可能因为连接问题而载入失败(在几秒钟尝试之后)。你不能在主线程中载入网络,并在屏幕冻结期间期望用户去等待它。所以须要后台线程。
2、异步线程载入
因为图片的载入是十分耗时间的,若把该操作放置在主线程中运行会减少CPU的利用率甚至拖慢帧率。能够通过GCD或者NSOperationQueue来实现异步载入最后再在主线程中同步发起渲染就可以。
3、延迟解压
一旦图片文件被载入使用。就必需要经过解码(解压)的过程,解码过程是一个相当复杂的任务。耗时长也占大量的内存。
对于PNG:载入相对长。文件相对更大,但解码比較快。
对于JPEG:载入快,图片小,但解码算法复杂耗时长。
因为iOS系统会让载入完毕的图片不会马上解压,而是到须要用的时刻才正式解压。所以会影响性能。
4、使用CATiledLayer实现异步载入和显示大型图片
略。
5、分辨率交换
略。
6、使用imageNamed实现缓存
imageName方法是能避免延迟载入,并且该方法在载入后会马上解压,但仅仅相应用资源束有效。所以网络图片无效。
之前我们提到使用
[UIImage imageNamed:] 载入图片有个优点在于能够立马解压图片而不用等到绘制的时候。可是[UIImage
imageNamed:] 方法有还有一个很显著的优点:它在内存中自己主动缓存了解压后的图片。即使你自己没有保留对它的不论什么引用。所以也要注意不能用于载入大图片,不然会占用大量的内存资源。
7、自己定义缓存
8、NSCache
NSCache和NSDictionary类似。都是直接通过键值进行訪问,但不同的是,NSCache所持有的对象在内存不足的时候。会自己主动将其释放。
9、文件格式与载入性能
略。
|
Layer Performance(图层性能)
1、隐形绘制
2、文本
CATextLayer与UILable都是直接将文本绘制在图层的寄宿层内,所以要避免频繁的修改,若该文本须要频繁修改,能够先将其放在一个子图层上,通过contentMode来等比例缩放寄宿层。
3、光栅化
我们提到了
CALayer 的shouldRasterize 属性(光栅化)。它能够解决重叠透明图层的混合失灵问题。启用
shouldRasterize 属性会将图层绘制到一个屏幕之外的图像。然后这个图像将会被缓存起来并绘制到实际图层的contents 和子图层。假设有非常多的子图层或者有复杂的效果应用,这样做就会比重绘全部事务的全部帧划得来得多。 可是光栅化原始图像须要时间。并且还会消耗额外的内存。 当我们使用得当时,光栅化能够提供非常大的性能优势(如你在第12章所见),可是一定要避免作用在内容不断变动的图层上,否则它缓存方面的优点就会消失。并且会让性能变的更糟。
为了检測你是否正确地使用了光栅化方式,用Instrument查看一下Color Hits Green和Misses Red项目。是否已光栅化图像被频繁地刷新(这样就说明图层并非光栅化的好选择,或则你无意间触发了不必要的改变导致了重绘行为)。
总结:会占用较多内存,要避免反复绘制。所以一旦应用要尽量降低又一次绘制,而多利用快照缓存。
4、离屏渲染
屏幕外渲染和我们启用光栅化时相似。除了它并没有像光栅化图层那么消耗大。子图层并没有被影响到,并且结果也没有被缓存,所以不会有长期的内存占用。可是,假设太多图层在屏幕外渲染依旧会影响到性能
对于那些须要动画并且要在屏幕外渲染的图层来说。你能够用
CAShapeLayer ,contentsCenter 或者shadowPath 来获得相同的表现并且较少地影响到性能。5、混合和过度绘制
开发人员应该尽量降低重叠图层的反复渲染,由于对于用户看来某些遮挡的图层的内容是无关紧要的,但渲染就会消耗资源。所以不管不论什么场景都建议例如以下操作:
当然光栅化是是详细场景二选择使用,不然也会引入别的性能问题,如占用大量内存。
6、降低图层数量
初始化图层,处理图层,打包通过IPC发给渲染引擎,转化成OpenGL几何图形,这些是一个图层的大致资源开销。其实,一次性可以在屏幕上显示的最大图层数量也是有限的。
确切的限制数量取决于iOS设备。图层类型。图层内容和属性等。
可是总得说来能够容纳上百或上千个,以下我们将演示即使图层本身并没有做什么也会遇到的性能问题。 7、对象回收
处理巨大数量的相似视图或图层时另一个技巧就是回收他们。对象回收在iOS颇为常见。
UITableView 和UICollectionView 都实用到,MKMapView 中的动画pin码也实用到,还有其它非常多样例。 |
- 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