动画

在使用Core Animation开发动画的本质就是将CALayer中的内容转化为位图从而供硬件操作。

 

通过beginAnimations:context:和commitAnimations创建的动画是显式动画。

通过UIView的animateWithDuration:animations:方法创建的动画隐式是动画。

 

CALayer本身并不包含在UIKit中,它不能响应事件。由于CALayer在设计之初就考虑它的动画操作功能,CALayer很多属性在修改时都能形成动画效果,这种属性称为“隐式动画属性”。

但是对于UIView的根图层而言属性的修改并不形成动画效果,因为很多情况下根图层更多的充当容器的做用,如果它的属性变动形成动画效果会直接影响子图层。

通过代理方法进行图层绘图只要指定图层的代理,然后在代理对象中重写* -(void)drawLayer:(CALayer )layer inContext:(CGContextRef)ctx方法即可。需要注意这个方法虽然是代理方法但是不用手动实现CALayerDelegate,因为CALayer定义中给NSObject做了分类扩展,所有的NSObject都包含这个方法。(分类的协议列表会添加到类中。)
 
另外设置完代理后必须要调用图层的setNeedDisplay方法,否则绘制的内容无法显示。
 
使用代理方法绘制图形、图像时在drawLayer:inContext:方法中可以通过事件参数获得绘制的图层和图形上下文。在这个方法中绘图时所有的位置都是相对于图层而言的,图形上下文指的也是当前图层的图形上下文。
 
改变CALayer的一个可做动画的属性,它并不能立刻在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值;这一切都是默认的行为,你不需要做额外的操作,这就是隐式动画。
 

layer的层次结构Layer Tree,这种层次结构分为以下三种:

  • Model Tree :也就是我们通常所说的layer。

  • Presentation Tree:呈现出来的layer,也就是我们做动画时你看到的那个layer,可以通过layer.presentationLayer获得。

  • Render Tree :私有,无法访问。主要是对Present

当设置removedOnCompletion 属性为NO以及fillMode属性为kCAFillModeForwards时,也并未改变model tree的位置,但是可以使动画结束后,防止presentation tree被移除并回到动画开始的位置。所以并不建议使用removedOnCompletion配合fillMode的方式来实现动画结束时,图层不跳转回原位的实现,我们应该在动画开始或者结束时重新设置它的位置。

 

//暂停动画
-(void)animationPause{
    //获取当前layer的动画媒体时间
    CFTimeInterval interval = [_aniLayer convertTime:CACurrentMediaTime() toLayer:nil];
    //设置时间偏移量,保证停留在当前位置
    _aniLayer.timeOffset = interval;
    //暂定动画
    _aniLayer.speed = 0;
}
//恢复动画
-(void)animationResume{
    //获取暂停的时间
    CFTimeInterval beginTime = CACurrentMediaTime() - _aniLayer.timeOffset;
    //设置偏移量
    _aniLayer.timeOffset = 0;
    //设置开始时间
    _aniLayer.beginTime = beginTime;
    //开始动画
    _aniLayer.speed = 1;
}
 
隐式动画的实现
CALayer的属性被修改时,它会调用-actionForKey:方法,并传递属性名称。
(1)图层首先检查它是否有委托,如果有CALayerDelegate,就在委托中查找-actionForLayer:forKey:方法,如果有-actionForLayer:forKey:调用它并返回。 
(2)如果没有委托或委托中没有实现-actionForLayer:forKey:方法,图层会检查actions字典,actions字典是属性名称对应行为的映射字典。 // self.colorLayer.actions = @{@"backgroundColor":transiton};
(3)如果actions字典中没有对应的属性名称,图层就检查style字典 
(4)如果style中也没有对应的行为,那么图层将直接调用-defaultActionForKey:方法, 
搜索完,-actionForKey:方法要么返回空(不会有动画发生),要么是CAAction协议对应的对象,然后CALayer拿这个结果对先前和当前的值做动画。
 
现在来看UIKit是如何禁用隐式动画的,每个UIView对它的关联图层都扮演了一个委托,并且提供了-actionForlayer:forKey的实现方法,当不存在一个动画块的实现中,UIView对所有图层行为返回nil,但是在block范围之内,它就返回一个非空值。
 
[CATransaction setDisableActions:YES];可通过此方法来禁用隐式动画----[UIView setAnimationsEnabled:NO];
动画执行的时间取决于当前事务的设置CATransaction,动画类型取决于图层行为。
 
在核心动画中,每个图层的修改都是事务 CATransaction的一部分,它可以同时对多个 layer的属性进行修改,然后成批的将将多个图层树包装起来,一次性发送到渲染服务进程。 CATransaction事务对象被分为 implicitexplicit两种类型,分别对应 隐式显式implicit transaction会被投递到线程的下一个 runloop完成处理:
 
系统提供 API来显式的使用事务类,并且手动提交给渲染服务进程,这种做法被称作 推进过渡推进过渡会生成一个默认时长为 0.25s时长的动画效果来完成属性值的修改。
 
图层属性被修改时,会朝着自己的代理对象请求一个 CAAction行为来帮助自己完成属性修改的行为。代理方法 actionForLayer:forKey:允许三种返回的数据格式来完成不同的修改动作:
  • 空对象      UIView在响应代理时默认会返回一个NSNull对象,表示属性修改后,不实现任何的动作,根据修改后的属性值直接更新视图。但UIView不总是会返回空对象,如果layer的修改发生在[UIView animatedXXX]接口的block中,每一个修改的属性值UIView都会返回对应的CABasicAnimation对象来进行动画修改//这是是显示动画

  • nil对象     手动创建并添加到视图上的CALayer或其子类在属性修改时,没有获取到具体的修改行为。此时被修改的属性会被CATransaction记录,最终在下一个runloop的回调中生成动画来响应本次属性修改。由于这个过程非开发者主动完成的,因此这种动画被称作隐式动画
  • CAAction的子类     如果返回的是CAAction对象,会直接开始动画来响应图层属性的修改。一般返回的对象多为CABasicAnimation类型,对象中包装了动画时长动画初始/结束状态动画时间曲线等关键信息。当CAAction对象被返回时,会立刻执行动作来响应本次属性修改

隐式动画的修改最终由CATransaction事务完成,它在主线程的runloop注册了一个监听者,具体回调发生在before waiting阶段。在回调中会将所有implicit transactions以动画的形式展示。

 

transaction回调结束时已经将图层树提交给渲染服务进程了,因此之后即便主线程发生卡顿,也不会影响渲染服务进程的工作。而早于transaction回调发生的卡顿会导致应用不能将图层树及时的提交到渲染服务进程,从而造成了动画开始前的界面停滞现象。

 

实际在应用使用中,即便是手指不离开屏幕,cell依旧能够展示各种动画。因此可以推断出transaction至少还注册了UITracking这个模式下的runloop监听处理

转载于:https://www.cnblogs.com/diyigechengxu/p/9239267.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值