Core Animation是直接作用在CALayer上的(并非UIView上)非常强大的跨Mac OS X和iOS平台的动画处理API,Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。CABasicAnimation(基本动画)、CAKeyframeAnimation(关键帧动画)、CAAnimationGroup(动画组)、CATransition(转场动画)
使用步骤如下(#import <QuartzCore/QuartzCore.h> ):
CoreAnimation基本介绍
CoreAnimation动画位于iOS框架的Media层
CoreAnimation动画实现需要添加QuartzCore.Framework
CoreAnimation基本上是Layer Animation
CoreAnimation分类
隐式动画:无需指定任何动画的类型,仅仅改变一个属性,然后CoreAnimation来决定
如何计时去做动画。
显示动画:对一些属性做指定的自定义动画,或创建非线性动画,比如沿着任意一条曲线移动。
CoreAnimation作用
与UIView动画相比,CoreAnimation能够实现更多复杂、好看、高效的动画效果。
阴影,圆角,带颜色的边框。
3D变化。
透明遮罩
多级非线性动画
CoreAnimation的三种动画
CABasicAnimation基本单一类型的动画,通过设定起始点,终点,时间,动画会沿着你这设定点进行移动。可以看做特殊的CAKeyFrameAnimation 。
CAKeyframeAnimation帧动画主要操作属性有keyPath和values值组合Keyframe顾名思义就是关键点的frame,你可以通过设定CALayer的始点、中间关键点、终点的frame,时间,动画会沿你设定的轨迹进行移动。
CAAnimationGroup组合动画 操作属性animations将CAAnimation类型的动画加入数组,以FIFO队列的方式执行。
CATransition这个就是苹果帮开发者封装好的一些动画
CABasicAnimation
三个重要的属性
@property(nullable, strong) id fromValue;动画的效果变化的初始值
@property(nullable, strong) id toValue;动画效果变化的结束值(绝对值)
@property(nullable, strong) id byValue;动画效果变化的结束值(相对值)
这三个属性必须要有一个有值,它们的值就是原来视图的放大,旋转等倍数或者角度。
fromValue和toValue不为空,动画的效果会从fromValue的值变化到toValue。
fromValue和byValue都不为空,动画的效果将会从fromValue变化到fromValue+byValue。
toValue 和byValue都不为空,动画的效果将会从toValue-byValue变化到toValue。
只有fromValue的值不为空,动画的效果将会从fromValue的值变化到当前的状态。
只有toValue的值不为空,动画的效果将会从当前状态的值变化到toValue的值。
只有byValue的值不为空,动画的效果将会从当前的值变化到(当前状态的值+byValue)的值。
想要实现不同的效果,最关键的地方在于CABasicAnimation对象的初始化方式中keyPath的设定。在iOS中有以下几种不同的keyPath,代表着不同的效果:
transform.rotation.x //以x轴为中心旋转
transform.rotation.y //以y轴为中心旋转
transform.rotation.z //以z轴为中心旋转
transform.scale.x //缩放x轴方向
transform.scale.y //缩放y轴方向
transform.scale.z //缩放z轴方向,这个一般不会用到。
transform.scale //x,y方向整体缩放,z方向没看到效果。
下面我们来简单的做一个动画
- (void)springAnimation{
CABasicAnimation base = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
/在动画结束之后是否让动画回到原处,这个属性的默认值是YES(回到原处),此时fillMode是没有作用的
如果设置为NO那么就需要设置一个fillMode属性,就是动画结束之后的状态,如果不设置,动画也会回到原处。/
base.removedOnCompletion = NO;
//动画结束之后状态的设置
base.fillMode = kCAFillModeForwards;
base.fromValue = @(2);
//动画时长
base.duration = 10;
//动画的重复次数
base.repeatCount = 100;
//给base动画设置延时启动
base.beginTime = 2 + CACurrentMediaTime();
//动画是否按原路径返回
base.autoreverses = YES;
//将动画添加到layer层forKey*是给动画添加一个标记,方便删除。
[self.redView.layer addAnimation:base forKey:@"basic"];
}
删除动画的方法
-(void)removeAnimationForKey:(NSString *)key;
key:你需要删除的动画的名称。
这个方法就是你把动画添加到那个视图的layer层上,就由那个视图的layer来调用。
-(void)removeAllAnimations;
这个法是删除这个视图layer层上的所有动画。
在这里简单介绍一下fillMode
注意:fillMode这个属性,必须要配合下面这个属性来使用。这个属性的默认值是YES(回到原处),此时fillMode是没有作用的如果设置为NO那么就需要设置一个fillMode属性,就是动画结束之后的状态,如果不设置,动画也会回到原处。
base.removedOnCompletion = NO;
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了
一个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加入一个
layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了layer,layer便处于动画初
始状态
kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画加入后开始之
前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.
CAKeyframeAnimation
这个动画可以让你在动画的一些关键的位置来改变动画的frame以此来达到我们想要的效果。
position是描述动画视图的位置信息的,简单理解就是和视图的中心点一样,所以我们通过改变position属性,就可以改变动画的位置。
与position相对应得是锚点也就是anchorPoint,anchorPoint、position、frame这三者之间有着如下的关系。
position.x = frame.origin.x + anchorPoint.x * bounds.size.width;
position.y = frame.origin.y + anchorPoint.y * bounds.size.height;
示例代码:下面是让一个视图左右震动的动画
CAKeyframeAnimation *keyFram = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//layer最初的position值
float position_x = self.redView.layer.position.x;
float position_y = self.redView.layer.position.y;
//layer向左晃动的偏移量
NSValue *leftValue = [NSValue valueWithCGPoint:CGPointMake(position_x - 30, position_y)];
//layer的原始位置
NSValue *originValue = [NSValue valueWithCGPoint:CGPointMake(position_x, position_y)];
//layer向右晃动的偏移量
NSValue *right = [NSValue valueWithCGPoint:CGPointMake(position_x + 30, position_y)];
//添加每一帧的Value值
[keyFram setValues:@[originValue,leftValue,originValue,right,originValue]];
keyFram.repeatCount = 10;
keyFram.repeatDuration = 1;
//为layer层添加动画
[self.redView.layer addAnimation:keyFram forKey:@"aaa"];
CAAnimationGroup
CAAnimationGroup是一个组合动画,所谓的组合动画就是将多个动画组合到一起让它产生很炫酷的效果。
注意:所有的组合动画,它的延迟启动,重复次数等属性,同一在组里面设置,不要在单一的动画里面设置,以免出现问题。
第一步:初始化组合动画
CAAnimationGroup group = [CAAnimationGroup animation];
第二步创建至少两个动画
CABasicAnimation base = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
/在动画结束之后是否让动画回到原处,这个属性的默认值是YES(回到原处),此时fillMode是没有作用的
如果设置为NO那么就需要设置一个fillMode属性,就是动画结束之后的状态,如果不设置,动画也会回到原处。/
base.removedOnCompletion = NO;
//动画结束之后状态的设置
base.fillMode = kCAFillModeForwards;
base.fromValue = @(M_PI);
//动画是否按原路径返回
base.autoreverses = YES;
/************************************************************************************************/
CAKeyframeAnimation *keyFram = [CAKeyframeAnimationanimationWithKeyPath:@"position"];
//layer最初的position值
float position_x = self.redView.layer.position.x;
float position_y = self.redView.layer.position.y;
//layer向左晃动的偏移量
NSValue *leftValue = [NSValue valueWithCGPoint:CGPointMake(position_x - 50, position_y)];
//layer的原始位置
NSValue *originValue = [NSValue valueWithCGPoint:CGPointMake(position_x, position_y)];
//layer向右晃动的偏移量
NSValue *right = [NSValue valueWithCGPoint:CGPointMake(position_x + 50, position_y)];
//添加每一帧的Value值
[keyFram setValues:@[originValue,leftValue,originValue,right,originValue]];
第三步将得到的动画放入group中
group.animations = @[base,keyFram];
//将group加到layer上
group.repeatCount = 100;
group.repeatDuration = 10;
[self.redView.layer addAnimation:group forKey:@"group"];
一.CoreAnimation介绍
CoreAnimation是一套图像渲染和动画基础框架,其在iOS和OSX平台用于显示对象和实现动画效果。使用CoreAnimation框架,动画的大部分帧渲染都是苹果为我们做好的。我们只需要配置几个动画参数(如开始和结束的点)并调用动画开始的方法。接下来就把剩余的工作交给CoreAnimation,操作全部实际绘图工作是在图形渲染硬件加速处理的。这个自动的图像加速器将会产生高帧频和顺滑的动画效果而不会加重CPU的负荷、或使APP卡顿。
CoreAnimation是在UIKit和APPKit框架之下,并且被很好的整合到Cocoa和Cocoa Touch的view中。同时CoreAnimation也给出了一些扩展的动画接口供我们使用。
下图是CoreAnimation在cocoa框架中的层级(图片来自苹果)
[图片上传失败...(image-5abe12-1524388748588)]
苹果对于CoreAnimation的介绍中首先讲述的是CALayer,因为CALayer是视图显示的基础、同时是CAAnimation动画产生的的载体。所有的动画都是作用在CALayer上,通过更改CALayer的属性,将每一帧渲染出来就形成了我们视觉的动画效果。但是这篇博客主要介绍CAAnimation,所以直接先忽略了前面的CALayer介绍,关于CALayer会在下一篇的文章中做详细介绍。
二.CoreAnimation动画
上面已经说过了,CoreAnimation是一套图形渲染与动画框架,CALayer负责图形的渲染显示,而CAAnimation及其子类则负责动画的实现。通过CAAnimatin及其子类我们能够相对简单的实现一些复杂的layer动画。
下面是整个CoreAnimation框架的所有动画类结构:
[图片上传失败...(image-9f9796-1524388748588)]
1.动画
CAAnimation是CoreAnimation的抽象超类、而CAPropertyAnimation又是CAAnimation的抽象子类,正如我们所知道的我们不应该直接使用抽象类,而应该使用它们的子类,如CABasicAnimation(基础动画)、CAKeyFrameAnimation(关键帧动画)、CASpringAnimation(弹簧动画)等。
1.1 CAAnimation
CAAnimation是CoreAnimation的抽象超类,也是整个动画的核心类,大部分的动画属性与方法都是在该类中实现的。CAAnimation之所以能够拥有这些动画相关的属性和方法是因为该类遵守了CAMediaTiming、 CAAction两个协议。CAMediaTiming协议主要实现一些控制动画执行时间的属性和函数(包含动画的开始时间 -> beginTime、执行时间 -> duration、执行速率 -> speed、执行时间偏移量 -> timeOffset、重复执行的次数 -> repeatCount、动画执行结束的处理 ->filleMode等),因此CAAnimation能够很好的处理时间与layer动画的关系;CAAction主要实现一些动作触发的响应接口,遵守该协议的对象可以指定CALayer响应的行为,如添加一个动画效果,或者执行其他的tasks。
timingFunction:属性用于设置动画执行的时间步调,创建该对象相对简单,可以直接使用kCAMediaTimingFunction系列宏指定动画执行的时间为**linear**',
easeIn', **easeOut**' and
easeInEaseOut'或者也可以使用贝塞尔曲线函数创建一个CAMediaTimingFunction对象贝塞尔曲线控制点获取
delegate:设置Animation的代理对象,这样我们可以获取动画执行过程的一些状态,包括动画的开始和结束,如果你只是想在结束时获得回调通知,也可以调用setCompletionBlock:函数,设置动画完成的block回调。
removedOnCompletion:动画执行结束是否移除动画,默认YES,但是该参数必须与fillMode = kCAFillModeForwards 或者 kCAFillModeBoth同时设置才能实现在动画结束不移除动画layer。
1.2 CAPropertyAnimation
CAPropertyAnimation是CAAnimation的抽象子类,使用该类可以创建一个操作CALayer属性值的Animation对象。该类主要实现接口用于指定实现动画的CALayer的属性。
1.3 CABasicAnimation
CABasicAnimation是CAPropertyAnimation的子类,该类实现了三个属性fromValue、byValue、toValue、用于描述一个单关键帧动画执行过程的三个属性值。
1.4 CAKeyFrameAnimation
CAKeyFrameAnimation是CAPropertyAnimation的子类,该类用于实现多关键帧动画。我们可以将动画主要的一些帧值添加到数组,赋值给values属性,再把每一帧对应的时间添加到keyTimes属性,(值得注意的是keyTimes的值必须在【0,1】之间,数组中的值按照数组的index依次增大,并且所有值加在一起的和等于一),如果想要为每一帧指定一个CAMediaTimingFunction对象,也可以创建对应的CAMediaTimingFunction对象并加到数组,然后赋值给timingFunctions属性。
CAKeyFrameAnimation最强大的地方在于他有一个path属性,当我们创建一个路径并赋值给path属性时,动画就会按照我们指定的路径轨迹执行。
1.5 CASpringAnimation
CASpringAnimation继承于CABasicAnimation类,正如它的名字Spring一样,该类主要用于实现一些类似弹簧的动画效果。mass属性相当于物体的重量,stiffness属性代表了弹簧的刚度,damping属性代表阻尼系数,initialVelocity属性代表动画的初始速度,settlingDuration是动画的预估执行时间。通过这些属性值,我们可以控制Spring动画的拉伸幅度,动画的执行时间等。
2.组动画
在动画使用的过程中,我们一般不会只使用一个动画,我们可能为复杂的动画效果创建多个Animation对象,然后将这些对象分别添加到Layer动画中,这种方式能够实现但是相对比较麻烦,我们可以使用组动画解决这个问题。 对于组合动画我们可以使用CATransition()和CAAnimationGroup, CAAnimationGroup将多个动画合并为一组,并且我们可以指定动画关联时间让组内的动画可以同时或者按步骤执行,CATransaction将多个layer-tree更改操作放在一起执行并自动更新到render tree。
2.1 CAAnimationGroup
CAAnimationGroup是CAAnimation的子类,该动画将多个动画放到一个animations数组,添加到layer,这些动画是并行执行的,当然你也可以设置例如beginTime这样的属性使动画按照一定的顺序执行。
2.2CATransition()
CATransition继承于CAAnimation,该动画主要实现一些过渡效果。你可以为type属性设置**fade**',
moveIn', **push**' and
reveal'来实现你想要的动画效果,同时你也可以设置subtype为**fromLeft**',
fromRight', **fromTop**' and
fromBottom'来指定动画的方向。startProgress、endProgress用于控制开始和结束的进度,范围必须在【0,1】。
3.动画执行时间
时间是动画的重要部分之一,我们可以通过CAMediaTiming协议的方法和属性指定动画的时间信息。在CoreAnimation中遵守这个协议的有两个类。CAAnimation类采用了这个协议,因此可以为Animations执行指定时间信息。CALayer类也采用了该协议,因此也可以为隐式动画指定Animations执行相关的时间信息,值得注意的是隐式动画优先采用默认的时间信息。
想想时间和动画的关系,理解layer对象是如何和时间相互作用的是非常重要的。每一个layer对象都有一个本地的时间去管理动画时间。通常在动画过程中的两个layer的本地时间非常接近,接近到我们可能察觉不到有什么不同。本地时间可以被父layer或者自己拥有的timing参数更改
CALayer类定义了convertTime:fromLayer: 和convertTime:toLayer:方法,为了确保时间值适用于layer。我们可以使用这些方法将layer的时间值转换为相对于本地时间或者另一个layer时间值的精确时间。使用这些方法需要考虑到对layer的本地时间的影响,同时这些方法会返回一个可以在其他的layer中使用的值。
一旦我们有了一个layer的本地时间值,我们就可以使用这个值去更新Animation对象或者layer的相关属性,使用CAMediaTiming协议属性,我们可以实现一些有趣的动画效果,包括:
1.我们可以使用beginTime属性设置Animation的开始时间。通常情况,Animations的开始时间是在下一次更新循环(the next update cycle),但是我们可以设置beginTime参数来延迟动画的执行时间。我们可以设置后一个动画的beginTime为前一个动画的结束时间,这样多个动画按照一定的顺序执行。
2.使用timeOffset属性可以在一组动画中设置相对于其他动画,延迟执行一定的时间段。
4.动画的添加与删除
我们创建好动画对象可以调用CALayer的addAnimation:forKey:方法将动画添加到layer上并指定动画的标记key用于后续的删除操作,如果要移除一个动画可以调用CALayer的removeAnimationForKey:方法移除key值对应的动画效果、或者你可以调用removeAllAnimations方法删除layer附带的所有动画效果,并且remove方法是立即生效的,动画立即结束执行。
三.CoreAnimation动画实现方式
通过更改Layer的属性值实现动画。(属性必须是Animatable,关于CALayer的可做动画的属性)
在这里更改属性值创建动画有两种:
第一种是隐式动画(Animating a change implicitly),隐式动画使用默认的时间(0.25秒)和动画属性去执行动画。当我们更改layer的属性值时,会触发隐式动画。当修饰的layer对象在layer-tree中时,更新会立即执行。如果layer对象的显示效果没有立即改变,CoreAnimation会为这些改变创建一个触发器并且添加一个或者多个隐式动画去执行。因此,像theLayer.opacity = 0.0;这样的更改会引发CoreAnimation为你创建一个Animation对象,并把这个Animation对象加入到下一次更新循环(next update cycle.)中去执行。
第二种是显式动画(Animating a change explicitly),需要你自己为使用的动画对象配置属性。如上的隐式动画如果要显示的执行,则需要我们创建一个Animation对象(如:CABasicAnimation)。并为这个对象配置动画参数。我们可以在把这个Animation添加到layer之前设置Animation的开始和结束值、动画的执行时间、或者设置其他的动画属性。当创建一个Animation对象,你需要指定动画的KeyPath并设置动画参数。去执行动画时,只需要调用addAnimation:forKey方法去将你想要执行的动画添加到layer上。显示动画的layer属性更新不像隐式动画,显示动画只是创建动画不会更改layer-tree中的属性值。在动画的结束CoreAnimation会移除动画并使用它当前的属性值重绘layer。如果你想要通过显示动画永久更改layer-tree中的属性值,那么你需要在动画结束时手动设置layer的属性。
隐式动画和显示动画通常是在当前的运行环(run loop)结束之后执行的,并且当前线程一定要有一个运行环去执行Animations。如果更改多个属性或者为layer添加Animations,那么这些改变或者添加的动画是并发执行的。当然也可以为每个动画设置特定的执行时间,具体的使用下面会有代码。
1.使用UIView的分类实现动画
尽管我们可以直接使用CAAnimation接口实现想要的动画效果,但是对于简单的动画使用CAAnimation还是需要额外的步骤,其实苹果已经为我们做了一些扩展,这些扩展在UIView的分类中实现,所以对于UIView自带的layer我们可以直接使用UIView去实现一些简单的动画。关于如何实现UIView自带的layer动画可以看这里How to Animate Layer-Backed Views.
下面是通过UIView动画分类接口实现的动画效果:
1.实现更改view透明度动画
[图片上传失败...(image-6e5bd-1524388748588)]
2.实现更换view背景色动画并延迟0.5秒执行
[图片上传失败...(image-ed044b-1524388748588)]
3.实现view移动动画
[图片上传失败...(image-3028ff-1524388748588)]
4.实现view旋转动画
[图片上传失败...(image-990e3e-1524388748588)]
5.实现view放大缩小动画
[图片上传失败...(image-b75ceb-1524388748588)]
[图片上传失败...(image-3faf1c-1524388748588)]
6.实现view弹簧效果动画
[图片上传失败...(image-562162-1524388748588)]
7.实现view系统删除动画
[图片上传失败...(image-40123c-1524388748588)]
8.实现过渡动画
[图片上传失败...(image-812c3e-1524388748588)]
9.事务实现view翻转动画
[图片上传失败...(image-d081eb-1524388748588)]
10.实现view组合动画
[图片上传失败...(image-326ab2-1524388748588)]
2.使用CAAnimation实现动画
UIView自带的动画是苹果给我提供的CAAnimation的封装,其动画实现相对来说比较简单方便,我们只需要在block内更改UIView的属性便可以实现一些简单的动画效果。但是其局限性也是十分明显的。对于一些复杂动画、高级动画UIView自带的动画就显得无能为力,这时CAAnimation的强大之处就无语言表了。其实CAAniamtion动画的使用也不是很困难,但是其对于控制动画执行时间、数学知识运用以及超常的想象力等要求就比较高了。
下面是对照UIView自带动画接口实现的一些动画,并简单列举了几个动画效果,对于UIView自带动画来说实现相对困难,以此来展示下CAAnimation的强大。
1.实现更改view透明度动画
[图片上传失败...(image-16f4e2-1524388748588)]
2.实现更改view的背景色
[图片上传失败...(image-37b1bd-1524388748588)]
3.实现view移动动画
[图片上传失败...(image-12f446-1524388748588)]
3.实现view曲线移动
[图片上传失败...(image-542f82-1524388748588)]
4.实现view旋转动画
[图片上传失败...(image-a01963-1524388748588)]
5.实现viewX轴翻转动画
[图片上传失败...(image-a7b9da-1524388748588)]
6.实现viewY轴翻转动画
[图片上传失败...(image-d6da7a-1524388748588)]
7.实现view放大缩小动画
[图片上传失败...(image-e91ab1-1524388748588)]
[图片上传失败...(image-766e3c-1524388748588)]
[图片上传失败...(image-c04787-1524388748588)]
8.实现view过渡动画
[图片上传失败...(image-440227-1524388748588)]
[图片上传失败...(image-ebee58-1524388748588)]
9.实现view弹簧动画
[图片上传失败...(image-e3308f-1524388748587)]
[图片上传失败...(image-34b50-1524388748587)]
10.事务动画
[图片上传失败...(image-4dfdd6-1524388748587)]
11.实现view组动画
[图片上传失败...(image-bb4ba2-1524388748587)]
[图片上传失败...(image-5dc573-1524388748587)]
四.自定义动画
上面说了那么多,但是创建的动画效果都还是比较普通的。对于CAAnimation类族,这些只是他们强大功能的冰山一角。但是无论多么复杂的动画其实都是上面这些基础动画的聚合,只要我么能够想象出动画的执行逻辑,我们就可以使用CAAnimation类族实现相对复杂的动画。本人的想象力有限,于是我模仿微博的tabbar中间item点击按钮的点击弹出菜单效果,实现了简单的弹出菜单,具体的代码:clone git代码。由于不能上传视频所以这里就不放效果图了,感兴趣的可以去下载工程运行试看。
五.总结
这篇博客主要介绍了CoreAnimation的动画使用,以及动画的实现。虽然CAAnimation类族的使用并不困难,但是还是有很多细节需要注意的。
1.显示动画在执行过程中的属性值修改并不会影响到layer-tree中的layer属性值,所以在动画执行结束,layer会回到原始状态。
2. 对于CALayer如果多次调用addAnimation: forKey:只会执行最后添加的Animation对象。
3. 对于一些属性值不是OC对象的需要将对应的结构体或者基本数据类型转化为OC对象,比较特殊的是颜色值,要将UIColor转化为CGColor并对其进行id强转。具体的属性值转换下表有对应的转换对象。
4. 其实动画中比较强大的属性是CALayer的transform属性,该属性可以实现3D动画效果,苹果也给出了一些transform的便捷keyPath,如rotation.x、scale.y、translation.z等。
5. 还有一点需要注意的是CAAnimation对象属性的设置必须在添加到CALayer之前,否则设置不起作用。