CoreAnimation基于一个假设,屏幕上的任何东西都可以做动画。
当你改变CALayer的一个可做动画的属性,它并不能立刻在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都是默认的行为。
之所以叫隐式动画是因为并没有指定任何动画的类型,仅仅改变了一个属性,然后CoreAnimation来决定如何并且何时去做动画。
实际上动画执行的时间取决于当前事务的设置,动画类型取决于图层行为。
事务实际上是CoreAnimation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性都不会立刻发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。事务是CATransaction类来做管理。CATransaction没有属性或者实例方法,并且也不能用+alloc和init方法创建它。但是用+begin和commit分别来入栈或者出栈。任何可以做动画的图层属性都会被添加到栈顶的事务。
完成块
基于UIView的block的动画允许你在动画结束的时候提供一个完成的动作。注意旋转动画要比颜色渐变快得多,这是因为完成块是在颜色渐变的事务提交并出栈之后才被执行。于是,用默认的事务做变换,默认的时间也就变成了0.25s。
图层行为
CoreAnimation通常对CALayer的所有属性(可动画的属性)做动画,但是UIView把它关联的图层的这个特性关闭了。把改变属性时CALayer自动应用的动画称作行为,当CALayer的属性被修改的时候,它会调用-actionForKey:方法,传递属性的名称。实际上如下几步:
图层首先检测它是否有委托,并且是否实现actionForLayer:forKey方法,如果有直接返回结果。
如果没有委托。或者没有实现actionForLayer:forKey方法,图层接着检查包含属性名称对应行为映射的actions字典。
如果actions字典没有包含对应的属性,那么图层接着在它的style字典接着搜索属性名。
最后,如果在style里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionkey:方法。
所以一轮完整的搜索结束之后,-actionForkey:要么返回空(这种情况下将不会有动画发生),要么是CAAction协议对应的对象,最后CALayer拿这个结果和对先前和当前的值做动画。
这就解释了UIKIT时如何禁用隐式动画的:每个UIView对它关联的图层都扮演了一个委托,并且提供了actionForLayer:forKey的实现方法。当不在一个动画块的实现中,UIView对所有图层行为返回nil,但是在动画block范围之内,返回了一个非空值。
当属性在动画块之外发生改变,UIView直接通过返回nil来禁用隐式动画。但如果在动画块范围之内,根据动画具体类型返回相应的属性。当然返回nil并不是禁用隐式动画唯一的方法,CATransacition有个方法叫做+setDisableActions,可以用来对所有属性打开或者关闭隐式动画。
总结以下几点:
UIView关联的图层禁用了隐式动画,对这种图层做动画的唯一方法就是使用UIView的动画函数(而不是依赖CATransaction),或者继承UIView,并覆盖actionForLayer:forKey方法,或者直接创建一个显示动画。
对于单独存在的图层,我们可以通过实现图层的actionForLayer:forKey委托方法,或者提供一个actions字典来控制隐式动画。
行为通常是一个被CoreAnimation隐式调用的显示动画对象。这里我们使用的是一个实现了CATransacition的实例,叫做推进过渡。
呈现与模型
当设置CALayer的属性,实际上是在定义当前事务结束之后图层如何显示的模型。CoreAnimation扮演了一个控制器的角色,并且负责根据图层行为和事务设置去不断更新视图的这些属性在屏幕上的状态。
呈现树通过图层树中所有图层的呈现图层所形成。注意呈现图层仅仅当图层首次被提交的时候创建,所以在那之前调用-presentationLayer将会返回nil。
如果修改代码让-hittest:直接作用于colorLayer而不是呈现图层,会发现当图层移动的时候它并不能正确显示。这时候你就需要点击图层将要移动的位置而不是图层本身来响应点击(这就是为什么要用呈现图层来响应交互的原因)
网友评论