学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

【读书笔记】核心动画高级技巧

Core Animation 是一个复合引擎,它的职责就是尽可能快地组合屏幕上不同的可视内容,这个内容是被分解成独立的图层,存储在一个叫做图层树的体系之中

iOS核心动画高级技巧书籍笔记,再结合项目最近遇到的动画做个总结。

图层树

每一个UIView都有一个CALayer实例的图层属性,也就是所谓的bcking layer,视图的职责就是创建并管理这个图层,以确保当子视图在层级关系中添加或者移除的时候,他们关联的图层也同样对应在层级关系树当中有相同的操作。

为什么iOS要基于UIVIew和CALayer提供两个平行的层级关系?
原因在于要做职责分离,在iOS和Mac OS两个平台上,事件和用户交互方式不同,一个基于多点触控,一个基于鼠标键盘,iOS有UIKit和UIView,Mac OS有Appkit和NSView。但是绘图,布局和动画,这部分就是类似的,把这种功能逻辑分开并应用到独立的Core Animation框架,苹果就能够在iOS和Mac OS之间共享代码。

UIVIew没有暴露出来的CALayer功能

  1. 阴影,圆角,带颜色的边框
  2. 3D变换
  3. 非矩形范围
  4. 透明遮罩
  5. 多级非线形动画

什么时候使用CALayer而不是UIView

  1. 开发同时可以在Mac OS上运行的跨平台应用
  2. 使用多种CALayer的子类,并且不想创建额外的UIVIew去包封装它们所有
  3. 做一些对性能特别挑剔的工作

寄宿图

contentsScale
contentsScale定义了寄宿图的像素尺寸和视图大小的比例。默认情况下它是一个值为1.0的浮点数

layer.contentsScale = [UIScreen mainScreen].scale;

contentsRect
CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域。它使用单位坐标。默认的 contentsRect 是{0, 0, 1, 1},这意味着整个寄宿图默认都是可见的

点(点就是虚拟的像素,也叫做逻辑像素,在Retina设备上,一个点等于2*2个像素)

单位(对于图片大小或是图层边界相关,当大小改变的时候,也不需要再次调整。单位坐标在OpenGL这种纹理坐标系统中用得很多)

contentsCenter
contetnsCenter是一个CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域。默认情况下, 是{0, 0, 1, 1}

设置为{0.25, 0.25, 0.5, 0.5}的效果

寄宿图赋值的方式

  1. layer.contents = (__bridge id)image.CGImage;
  2. Core Graphics直接绘制寄宿图,通过继承UIView并实现 -drawRect: 方法 来自定义绘制

-drawRect:方法没有默认的实现,因为对UIView来说,寄宿图并不是必须的,它不在意那到底是单调的颜色还是有一个图片的实例。如果UIView检测到drawRect方法被调用,它就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以contentsScale的值

不同于UIView,当图层显示在屏幕上时,CALayer不会自动重绘它的内容,它把重绘的决定权交给了开发者

图层几何学

UIView三个布局属性frame,bounds,center,CALayer对应frame,bounds,position

视觉效果

阴影

阴影的表现由三个属性 shadowColor,shadowOffset,shadowRadius

shadowOffset控制着阴影的方向和距离,是一个CGSize值,宽度控制横向位移,高度控制纵向位移,默认值为{0,-3},意即阴影相对于Y轴有3个点的向上位移。

shadowRadius控制阴影的模糊度,为0的时候,阴影就和视图一样有一个非常确定的边界线,值越大,边界线就会越来越模糊和自然

阴影和裁剪同时存在的时候?
阴影通常就是在Layer的边界之外,如果你开启了masksToBounds属性,所有图层中突出来的内容都会被裁减掉。这个时候需要用到两个图层,一个只画阴影的空的外图层,和一个用maskToBounds裁剪内容的内图层。

shadowPath属性
用来指定阴影的形状,是一个CGPathRef类型(一个指向CGPath的指针),CGPath是一个Core Graphics对象,用来指定任意的一个矢量图形。

图层蒙版

masksToBounds属性可以沿边界裁剪图形,使用图层蒙版可以将展示内容不是在一个矩形或圆角矩形。

使用一个32位有alpha通道的png图片通常是创建一个无矩形视图最方便的方法,可以给它指定一个透明蒙版来实现。

mask图层定义了父图层的部分可见区域。mask图层的属性是无关紧要的,真正重要的是图层的轮廓,

蒙版层不局限于静态图,任何有图层构成的都可以作为mask属性,这意味着你的梦版可以通过代码甚至是动画实时生成

组透明

UIView有一个叫做alpha的属性来确定视图的透明度,CALayer有一个等同的属性叫做opacity。这两个属性都是影响子层级的。

当显示一个50%透明度的图层时,图层的每个像素都会一半显示自己的颜色,另一半显示图层下面的颜色。当你设置了一个图层的透明度,你希望它包含的整个图层树像一个整体一样的透明效果,可以通过设置CALayer的shouldRasterize属性来实线组透明效果。如果它被设置成YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片,这样就没有透明度混合的问题了。

CALayer 有一个叫做 doubleSided 的属性来控 制图层的背面是否要被绘制。这 是一个 BOOL 类型,默认为 YES ,如果设置为 NO ,那么当图层正面从相机视角 消失的时候,它将不会被绘制

专用图层

CAShapeLayer

一个通过矢量图形而不是bitmap来绘制的图层子集,指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。

优点
CAShapeLayer使用了硬件加速,绘制同一图形会比用CoreGraphics快很多
不需要像普通CALayer一样创建一个寄宿图,所以无论有多大,都不会占用太多内存。

CAReplicatorLayer

可以高效生成许多相似的图层。

CATiledLayer

为载入大图造成的性能问题提供了一个解决方案,将大图分解成小片,然后将他们单独按需载入。CATiledLayer支持多线程绘制,drawLayer:inContext:方法可以在多个线程中同时地并发调用。

隐式动画

隐式动画

把改变属性时 CALayer 自动应用的动画称作隐式动画

动画执行的时间取决于当前事务的设置,动画类型取决于图层行为。

隐士动画如何实现
当 CALayer 的属性被修 改时候,它会调用 -actionForKey: 方法,传递属性的名称。剩下的操作都 在 CALayer 的头文件中有详细的说明,实质上是如下几步:

  1. 图层首先检测它是否有委托,并且是否实现 CALayerDelegate 协议指定的 - actionForLayer:forKey 方法。如果有,直接调用并返回结果。
  2. 如果没有委托,或者委托没有实现 -actionForLayer:forKey 方法,图层接 着检查包含属性名称对应行为映射的 actions 字典。
  3. 如果 actions字典 没有包含对应的属性,那么图层接着在它的 style 字典接 着搜索属性名。
  4. 最后,如果在 style 里面也找不到对应的行为,那么图层将会直接调用定义 了每个属性的标准行为的 -defaultActionForKey: 方法。

一轮完整的搜索结束之后, -actionForKey: 要么返回空(这种情况下将不 会有动画发生),要么是 CAAction 协议对应的对象,最后 CALayer 拿这个结果 去对先前和当前的值做动画。

UIKit是如何禁用隐式动画的?
每个 UIView 对它关联的图层都扮 演了一个委托,并且提供了 -actionForLayer:forKey 的实现方法。当不在一个 动画块的实现中, UIView 对所有图层行为返回 nil ,但是在动画block范围之 内,它就返回了一个非空值

事务 (CATransaction)

事务实际上是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性都不会立刻发生变化,,而是当事务一旦提交的时候开始用一个动画过度到新值。

Core Animation在每个run loop周期中自动开始一次新的事务(run loop是iOS负责 收集用户输入,处理定时器或者网络事件并且重新绘制屏幕的东西),即使你不显 式的用 [CATransaction begin] 开始一次事务,任何在一次run loop循环中属性 的改变都会被集中起来,然后做一次0.25秒的动画

呈现于模型

改变一个图层的属性并没有立刻生效,而是通过一段时间渐变更新,如何实现的?
当你改变一个图层的属性,属性值的确是立刻更新的,但是屏幕上没有马上发生改变,这是因为你设置的属性并没有直接调整图层的外观,相反它只是定义了图层动画结束后将要变化的外观。

设置CALayer属性,实际上是在定义当前事务结束之后图层如何显示的模型,Core Animation扮演了一个控制器的角色,并且负责根据图层行为和事务设置去不断更新视图的这些属性在屏幕上的状态。

每个图层属性的显示值都被存储在一个叫做呈现图层的独立图层当中,他可以通 过 -presentationLayer 方法来访问。它的属性值代表了在任何指定时刻当前外观效果。换句话说,你可以通过呈现 图层的值来获取当前屏幕上真正显示出来的值

显式动画

它能够对一些属性做指定的自定义动画,或者创 建非线性动画,比如沿着任意一条曲线移动

过渡动画

过渡动画做基础的原则就是对原始的图层 外观截图,然后添加一段动画,平滑过渡到图层改变之后那个截图的效果

性能调优

尽可能把屏幕渲染的工作交给硬件(GPU)去处理

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

应用内的FPS显示并不能够完全真实测量 出Core Animation性能,因为它仅仅测出应用内的帧率。我们知道很多动画都在应 用之外发生(在渲染服务器进程中处理)