美文网首页
核心动画技巧 学习笔记

核心动画技巧 学习笔记

作者: 管乐_VICTOR | 来源:发表于2019-01-07 18:18 被阅读12次

参考网址:https://zsisme.gitbooks.io/ios-/content/chapter14/image-ioi.html

zPosition可以改变图层的顺序,但是不能改变事件传递顺序;

子view超出父view的bounds响应点击;

  • (void)layoutSublayersOfLayer:(CALayer *)layer;

当图层的bounds发生改变,或者图层的 -setNeedsLayout 方法被调用的时候,这个函数会被执行。这使得你可以手动摆放或者重新调整子图层的大小,但是不能像UIView 的autoresizingMask 和 constraints 属性做到自适应屏幕旋转。

如果子视图或寄宿图的超过了父视图的边界,边框还是会围着父视图的边界画出来的。

如果使用阴影,而超过了父视图的边界,阴影的效果也会直接被取消掉;

阴影其实很消耗资源,特别是有很多个子视图的时候;阴影可以设置成方的,圆的,或者你想要的其他形状;

CALayer有个mask属性,可以截取下面图层背景,上面图层颜色或图片。这个属性可以使动态生成想要的图形和图画;

CALayer蒙版图真正厉害的地方在于,蒙版图不限于静态图,意味着可以通过代码或者动画实时生成;

最近过滤算法;

view.layer.magnificationFilter = kCAFilterNearest;

设置了 magnificationFilter 属性会使模糊的图片变得清晰;

UIView有个alpha属性来确定视图的透明度,CALayer也有一个类似的属性叫opacity,这两个属性都是影响子层级的,就是如果一个图层设置了opacity属性,那么它的子层级都会受此影响。

// 旋转

CGAffineTransformRotate(<#CGAffineTransform t#>, <#CGFloat angle#>);

// 缩小

CGAffineTransformScale(<#CGAffineTransform t#>, <#CGFloat sx#>, <#CGFloat sy#>)

// 移动像素点 如果这句在后面的话,会受上面两句的影响

CGAffineTransformTranslate(<#CGAffineTransform t#>, <#CGFloat tx#>, <#CGFloat ty#>)

// 混合两个已经存在的矩阵

CGAffineTransformConcat(<#CGAffineTransform t1#>, <#CGAffineTransform t2#>)

// 3D变换

CATransform3DMakeRotation(<#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)

CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)

CATransform3DMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)

我们可以使用m34元素来做透视

固体对象,我们可以用Interface Builder来构建立方体的面,当然也可以用代码来写。可以在每个面上定义视图和控件等用户界面元素;

CAShapeLayer

CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALayer的内容中绘制一个路径,相比之下,使用CAShapeLayer有以下一些优点:

① 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。

② 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用过多的内存。

③ 不会被图形边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层不会像在使用Core Graphics的普通CALayer一样被裁减掉。

④ 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

CAShapeLayer的圆角可以单独指定一个角。

CATextLayer

CATextLayer 要比UILabel渲染得快得多。很少有人知道在iOS6及之前的版本,UILabel其实是通过WebKit来实现绘制的,这样就造成了当有很多文字的时候就会有极大的性能压力。

而 CATextLayer 使用了 Core text ,并且渲染得非常快。

CATextLayer 的 string 属性并不是你想象的 NSString 类型,而是 id 类型。 这样,你既可以用 NSString 也可以用 NSAttributedString 来制定文本了(注意, NSAttributedString 并不是 NSString 的子类)。

如果被像素化了就要使用contentsScale属性来修改一下,就OK了。

CAReplicatorLayer 重复图层,可以用来做同一图片的轮状排列,也可以用来做反射,反射就相当于是重复次数是2,然后角度刚好是180°。😁

CATiledLayer

有时候需要绘制一个很大的图片,如果在主线程调用UIImage的 -imageNamed: 方法或者 -imageWithContentOfFile: 方法 ,将会阻塞你的用户界面,至少也会引起卡顿现象。

CAEAGLLayer

当iOS要处理高性能图形绘制,必要时就是OpenGL。应该说它应该是最后的杀手锏,至少对于非游戏的应用来说是的。

如果想更好的控制动画时间,使用显式动画会更好。

-renderInContext: 捕获了图层的图片和子图层,但是不能对子图层正确的处理变换效果,而且对视频和OpenGL内容也不起作用。但是用 CATransition ,或者用私有的截屏方式就没有这个限制了。

CAMediaTiming 协议

CAMediaTiming 协议定义了在一段动画内用来控制逝去时间的属性的集合,CALayer 和 CAAnimation 都实现了这个协议,所以时间可以被任意基于一个图层或者一段动画的类控制。

speed 是一个时间的倍数,默认1.0,减少它会减慢图层/动画的时间,增加它会加快速度。如果speed为2.0,那么对于一个duration为1的动画,实际上0.5秒的时候就已经完成了。

timeOffset 和 beginTime 类似,但是和增加 beginTime 导致的延迟动画不同,增加 timeOffset 只是让动画快进到某一点,例如,对于一个持续1秒的动画来说,设置 timeOffset 为0.5 的话,就意味着动画从一半的地方开始。

和 beginTime 不同的是,timeOffset 并不受 speed 的影响。所以如果你把 speed 设为2.0,把 timeOffset 设置为0.5,那么你的动画将从动画最后结束的地方开始,因为1秒的动画实际上被缩短到0.5秒。然而即使使用了 timeOffset 让动画从结束的地方开始,它仍然播放了一个完整的时长,相当于这个动画循环了一圈,然后从头开始播放。

当使用 NSTimer 的时候,一旦有机会计时器就会开启,但是 CADisplayLink 却不一样:如果它丢失了帧,就会直接忽略它们,然后在下一次更新的时候接着运行。

NSDefaultRunLoopMode // 标准优先级

NSRunLoopCommonModes // 高优先级

UITrackingRunLoopMode // 用于 UIScrollView 和别的控件的动画。

关于绘图和动画的两种处理方式:CPU(中央处理器)和GPU(图形处理器)。

CPU所做的工作都在软件层面,GPU在硬件层面;

动画和屏幕上组合的图层实际上被一个单独的进程管理,而不是你的应用程序。这个进程就是所谓的渲染服务。

当运行一段动画的时候,这个过程会被四个分离的阶段被打破:

① 布局。这是准备你的视图/图层的层级关系,以及设置图层属性(位置,背景色,边框等待)的阶段。

② 显示。这是图层的寄宿图片被绘制的阶段。绘制有可能会涉及你的 -drawRect:和 -drawLayer:inContext:方法的调用路径。

③ 准备。这是 Core Animation 准备发送动画数据到渲染服务的阶段。这同时也是 Core Animation 将要执行一些别的事务例如解码动画过程中将要显示的图片的时间点。

④ 提交。这是最后阶段,Core Animation 打包所有图层和动画属性,然后通过 IPC(内部处理通信)发送到渲染服务进行显示。

以下CPU的操作都会延迟动画的开始时间:

① 布局计算。如果你的视图层级过于复杂,当视图呈现或者修改的时候,计算图层帧率就会消耗一部分时间。

② 视图懒加载。这对内存使用和程序启动时间很有好处,都是当呈现到屏幕上之前,按下按钮导致的许多工作都不能被及时响应。比如控制器从数据库中获取数据,或者视图从一个nib文件中加载,或者涉及IO的图片显示,都会比CPU正常操作慢得多。

③ Core Graphics绘制。如果对视图实现了 -drawRect:方法,或者 CALayerDelegate 的 -drawLayer:inContext: 方法,那么在绘制任何东西之前都会产生一个巨大的性能开销。为了支持对图层内容的任意绘制,Core Animation 必须创建一个内存中等大小的寄宿图片。然后一旦绘制结束之后,必须把图片数据通过IPC传到渲染服务器。在此基础上,Core Graphics 绘制就会变得十分缓慢,所以在一个对性能十分挑剔的场景下这样做十分不好。

④ 解压图片。 对于一个较大的图片,都会占用一定的时间。

IO 比内存访问更慢,所以如果动画涉及到IO,就是一个大问题。总的来说,这就需要使用聪敏但尴尬的技术,也就是多线程,缓存和投机加载(提前加载当前不需要的资源,但是之后可能需要用到)。

Instruments 工具选项窗口

① Time Profiler 时间分析器。用来测量被方法/ 函数打断的CPU使用情况。

② Core Animation。用来调试各种 Core Animation 性能问题。

③ OpenGL ES驱动。用来调试GPU性能问题。这个工具在编写Open GL 代码的时候很有用,但有时也用来处理Core Animation的工作。

Instruments 可以创建我们自定义的工具集。可以拖拽别的工具到左侧边栏,就可以并行使用了。

时间分析器工具

① 通过线程分离。 这可以通过执行的线程进行分组。如果代码被多线程分离的话,那么就可以判断到底是哪个线程造成了问题。

② 隐藏系统库。 可以隐藏所有苹果的框架代码,来帮助我们寻找哪一段代码造成了性能瓶颈。由于我们不能优化框架方法,所以这对定位到我们能实际修复的代码很有用。

③ 只显示Objective-C 代码。 隐藏除了Objective-C之外的所有代码。大多数内部的Core Animation 代码都是用C或者C++ 函数,所以这对我们集中精力到我们代码中显式调用的方法就很有用。

一些必要的情况下,相比 Core Animation 和OpenGL, Core Graphics要慢了不少。但是在iOS中,软件绘图通常是由 Core Graphics 框架来完成的。

软件绘图代价昂贵,除非必要,不要轻易重绘。

GCD 是全局C函数,NSOperationQueue是Objective-C的接口。

PNG图片解码会相对较快,Xcode会把PNG图片进行解码优化后引入工程,JPEG图片更小,加载更快,但是解压步骤需要消耗更长时间,因为JPEG解压算法会比基于zip的PNG算法更加复杂。

+imageNamed: 方法

[UIImage imageNamed:] 加载图片有个好处是,可以立即解压而不用等到绘制的时候再解压,它在内存中自动缓存了解压后的图片,即使你自己没有保留对它的任何引用。nib 文件中引用的图片也是用的这个机制,所以很多时候都是隐式的使用它。

[UIImage imageNamed:] 缓存用来存储应用界面的图片(按钮、背景等等)。如果对照片这种大图也用这种缓存的话,那么iOS系统可能需会移除这些图片来节省内存,那么在切换界面的时候性能就会下降,因为这些图片都需要重新加载,对传送器的图片使用一个单独的缓存机制就可以把它和应用图片的生命周期解耦。

JPEG对于噪点大的图片效果更好;但是PNG更适合于扁平颜色,锋利的线条或者一些渐变色的图片。

JPEG 2000:

PVRTC (PowerVR Texture Compression) 由PowerVR芯片支持,每个iOS设备都使用了Imagination Technologies PowerVR 图像芯片作为GPU。

为了检查你是否正确的使用了光栅化方式,用Instrument查看一下Color Hits Green 和 Misses Red 项目,是否已光栅化图像被频繁的刷新,这样就说明图层并不是光栅化的最好选择,或者你无意中触发了不必要的改变导致了重绘的行为。

最好不要用透明,除非非常有必要。

参考网址:https://zsisme.gitbooks.io/ios-/content/chapter14/image-ioi.html

相关文章

网友评论

      本文标题:核心动画技巧 学习笔记

      本文链接:https://www.haomeiwen.com/subject/aqkprqtx.html