基本概念
一、什么是核心动画
Core Animation(核心动画)是一组功能强大、效果炫酷的动画API,无论在iOS系统或者在你开发的App中,都有大量应用。
Core Animation位于AppKit和UIKit之下,并紧密集成到Cocoa和Cocoa Touch的视图工作流中。当然,Core Animation也具有接口,可以扩展应用视图所显示的功能,并让您更好地控制应用的动画。
可以看到,核心动画位于UIKit的下一层,相比UIView动画,它可以实现更复杂的动画效果。
二、UIView 和 CALayer 的关系
UIView本身完全是由CoreAnimation来实现. 真正的绘图部分, 是由一个CALayer类来管理. UIView更像是一个CALayer的管理器, 所以访问它的与绘图和坐标相关的属性, 如frame, bounds等, 实际上都是在访问其所包含的CALayer的相关属性. 因此, 可以在所有UIView的子类上实现动画效果. UIView继承自UIResponder, 能接收并响应事件, 负责显示内容的管理。
简言之:layer负责绘制内容,view负责显示和交互。
总体来说核心动画的优点有:
1)性能强大,使用硬件加速,可以同时向多个图层添加不同的动画效果
2)运行在后台线程中,在动画过程中可以响应交互事件(UIView动画默认动画过程中不响应交互事件)。
核心动画的层次关系:
从这个层次关系中可以看出如下几个特点:
1、CAAnimation 相关的时间控制属性、速度属性、重复性控制属性等属性都是来之于协议 CAMediaTiming ,CAAnimation 实现了这个协议,所有它的子类都具有这些属性。
2、CAAnimationGroup( 动画组)、CAPropertyAnimation、CATransition(转场动画)直接继承与CAAnimation。动画组和转场动画都可以直接使用,CAPropertyAnimation 一般不直接使用。
3、CABasicAnimation、CAKeyFrameAnimation 继承于CAPropertyAnimation,是我们最常使用的核心动画。其实CABasicAnimation就是特殊的CAKeyFrameAnimation 动画(就是具有开始状态和结束状态的CAKeyFrameAnimation 动画)。
4、CASpringAnimation 是 CABasicAnimation的子类,是iOS9.0之后新增的类,它实现了弹簧效果的动画。
三、核心动画使用步骤
1.初始化CAAnimation对象
一般使用animation方法生成实例
+ (instancetype)animation;
如果是CAPropertyAnimation的子类,还可以通过animationWithKeyPath生成实例
+ (instancetype)animationWithKeyPath:(nullableNSString*)path;
2.设置动画的相关属性
设置动画的执行时间,重复性、动画速度、执行路径,keyPath的目标值,代理等等
3.动画的添加和移除
CALayer添加动画使用addAnimation方法
- (void)addAnimation:(CAAnimation*)anim forKey:(nullableNSString*)key;
调用CALayer的removeAnimation 停止动画,并且把Layer上的动画移除。
- (void)removeAnimationForKey:(NSString*)key; //按照key移除动画
- (void)removeAllAnimations; //移除所有动画
4、核心动画类的常用属性
keyPath:可以指定keyPath为CALayer的属性值,并对它的值进行修改,以达到对应的动画效果,需要注意的是部分属性值是不支持动画效果的。
以下是具有动画效果的keyPath:
CATransform3D Key Paths : (transform.rotation.x)
rotation.x 设置NSNumber为其值为旋转的对象,以弧度为单位,在x轴中。
rotation.y 设置NSNumber为其值为旋转的对象,以弧度为单位,在y轴中。rotation.z 设置NSNumber为其值为旋转的对象,弧度为z轴。
rotation 设置NSNumber为其值为旋转的对象,弧度为z轴。该字段与设置rotation.z字段相同。
scale.x 设置为一个NSNumber对象,其值为x轴的比例因子。
scale.y 设置为一个NSNumber对象,其值为y轴的比例因子。
scale.z 设置为一个NSNumber对象,其值为z轴的比例因子。
scale 设置为一个NSNumber对象,其值是所有三个比例因子的平均值。translation.x 设置为一个NSNumber对象,其值是沿x轴的平移因子。
translation.y 设置为一个NSNumber对象,其值为沿y轴的平移因子。
translation.z 设置为一个NSNumber对象,其值是沿z轴的平移因子。
translation 设置为NSValue包含NSSize或CGSize数据类型的对象。该数据类型表示要在x和y轴上转换的数量。
CGPoint Key Paths :
position.x
position.y
position
CGRect Key Paths :
bounds.size.width
bounds.origin.x
bounds.origin.y
bounds.origin 矩形的起始点CGPoint
bounds.size.width
bounds.size.height
bounds.size 矩形的大小为CGSize。
opacity
backgroundColor
cornerRadius
borderWidth
contents
shadowColor
shadowOffset
shadowOpacity
shadowRadius
strokeStart CAShapeLayer 中的属性
strokeEnd CAShapeLayer 中的属性
lineWidth CAShapeLayer 中的属性
path
duration:动画的持续时间
repeatCount:动画的重复次数
timingFunction:动画的时间节奏控制
timingFunctionName的enum值如下:
kCAMediaTimingFunctionLinear 匀速
kCAMediaTimingFunctionEaseIn 慢进快出(匀加速快出)
kCAMediaTimingFunctionEaseOut 快进慢出(匀加速快进)
kCAMediaTimingFunctionEaseInEaseOut 慢进慢出 (中间快)
kCAMediaTimingFunctionDefault 默认值(慢进慢出)
fillMode:填充模式视图在非Active时的行为
kCAFillModeForwards 向前填充(保持动画结束后的状态)
kCAFillModeBackwards 向后填充(保持动画开始前的状态)
kCAFillModeBoth 既向前填充又向后填充
kCAFillModeRemoved 动画结束后,移除动画
removedOnCompletion:动画执行完毕后是否从图层上移除,默认为YES(视图会恢复到动画前的状态),可设置为NO(图层保持动画执行后的状态,前提是fillMode设置为kCAFillModeForwards)
beginTime:动画延迟执行时间(通过CACurrentMediaTime() + your time 设置)
delegate:代理
CAAnimationDelegate 有两个代理方法如下:
- (void)animationDidStart:(CAAnimation*)anim;//动画开始
- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag;//动画结束
有关核心动画的时间控制,推荐大家看看:核心动画的时间控制 这篇文章写的非常到位。
接下来针对各种动画举例说明使用方法
CABasicAnimation
CABasicAnimation动画中,执行动画过程的属性就是fromValue, toValue。keyPath 指定什么属性需要做动画。CABasicAnimation就是特殊的关键帧动画,可以看成关键帧的values属性就是fromValue 和 toValue这两个值。
下面以改变圆角边cornerRadius和背景色backgroundColor举例:
- (void)setCRWithBasicAnimation {
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(30, 40, self.containerView.bounds.size.width - 30 *2, self.containerView.bounds.size.height - 40*2);layer.backgroundColor = UIColor.brownColor.CGColor;
[self.containerView.layer addSublayer:layer];
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
basicAnimation.fromValue=@(0);
basicAnimation.toValue=@(50);
basicAnimation.duration=2.f;
basicAnimation.fillMode = kCAFillModeForwards;
basicAnimation.removedOnCompletion=NO; [layeraddAnimation:basicAnimationforKey:@"basicAni"];
}
圆角边动效图:
背景色动效图:
CAKeyFrameAnimation
与CABasicAnimation最大的区别在于:可以设定除了“起始点”和“终点”外的 中间关键点(可以设置多个),每一帧都对应一段时间,动画会按照values和keyTimes的设置运动。
CAKeyFrameAnimation的重要属性:
values:关键帧数组对象,里面每一个元素即为一个关键帧,动画会在对应的时间段内,依次执行数组中每一个关键帧的动画。
path:动画路径对象,可以指定一个路径,在执行动画时路径会沿着路径移动,Path在动画中只会影响视图的Position。
keyTimes:设置关键帧对应的时间点,范围:0-1。keyTimes里的值必须是升序设置。因为对应于values里关键帧所设置的,所以时间点是升序的。values 的每一个对象都对应于keyTimes里的一个时间点。 它们是一一对应的关系。如果没有设置该属性,则每一帧的时间平分。
使用keyFrameAnimation 改变 layer的rotation
- (void)changeRotationWithKeyAnimation {
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100, (self.containerView.bounds.size.height - 100)/2.0, self.containerView.bounds.size.width - 100*2, 100);
layer.backgroundColor = [UIColor colorWithRed:0.4 green:0.8 blue:0.1 alpha:1].CGColor;
[self.containerView.layer addSublayer:layer];CAKeyframeAnimation *keyFrameAni = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
keyFrameAni.values=@[@(M_PI_4),@(M_PI_2),@(-M_PI),@(1.5*M_PI)];
keyFrameAni.keyTimes=@[@0,@0.35,@0.75,@1];
keyFrameAni.duration=3.5f;
keyFrameAni.fillMode = kCAFillModeBoth;
keyFrameAni.removedOnCompletion=NO;
[layeraddAnimation:keyFrameAniforKey:@"kfAnimationRotation"];
}
rotation 旋转效果图:
使用path属性结合贝塞尔曲线做椭圆运动
- (void)changePathWithKeyAnimation {
CALayer *layer = [CALayer layer];
layer.frame=CGRectMake(80, (self.containerView.bounds.size.height-40)/2.0,40, 40); [self.containerView.layer addSublayer:layer];UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 40, ScreenW - 40, ScreenH - 300)];
CAKeyframeAnimation *keyFrameAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyFrameAni.path= path.CGPath;
keyFrameAni.duration=2.0f;
keyFrameAni.repeatCount=1000;
[layeraddAnimation:keyFrameAniforKey:@"kfAnimation"];
}
path做椭圆运动效果图:
CASpringAnimation
CASpringAnimation是iOS9新加入动画类型,是CABasicAnimation的子类,用于实现弹簧动画。
CASpringAnimation的重要属性:
mass: 质量(质量越大,弹簧惯性越大,运动的幅度越大)
stiffness: 弹性(劲度)系数(弹性系数越大,弹簧的运动越快)
damping: 阻尼系数(阻尼系数越大,弹簧的停止越快)
initialVelocity: 初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)
settlingDuration: 结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)
CASpringAnimation和UIView的SpringAnimation对比:
1.CASpringAnimation 可以设置更多影响弹簧动画效果的属性,可以实现更复杂的弹簧动画效果,且可以和其他动画组合。
2.UIView的SpringAnimation实际上就是通过CASpringAnimation来实现。
使用CASpringAnimation制作弹框缩放效果:
- (void)springAnimation {
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(50, (self.containerView.bounds.size.height - 180)/2.0, self.containerView.bounds.size.width - 50*2, 180);
[self.containerView.layer addSublayer:layer];CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
springAnimation.fromValue=@(0.95);
springAnimation.toValue=@(1);
springAnimation.mass=1;//质量越大,惯性越大
springAnimation.stiffness=50;//劲度系数,值越大,运动的越快 springAnimation.damping=10;//阻尼系数 值越大 停止的越快 springAnimation.initialVelocity=50;//初始速度
springAnimation.duration= springAnimation.settlingDuration; [layeraddAnimation:springAnimationforKey:@"springAnimation"];
}
弹框缩放动画效果:
CAAnimationGroup
animationGroup动画组,顾名思义就是动画的组合。它可以实现多个动画同时添加在一个layer上的功能。并且是并发执行的。没有先后顺序之分。
主要属性animations:是一个动画数组,用来接收需要添加到动画组中的核心动画。
还有一点需要注意的是:动画组的duration 需要设置的时间需要比它里包含的所有单个动画的duration要大或则等于,否则肯定有动画没有做完,然后动画组就结束了。
使用CAAnimationGroup 做一个 位移+背景变换 + 缩放的弹簧效果
- (void)groupAnimation {
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(50, (self.containerView.bounds.size.height - 180)/2.0, self.containerView.bounds.size.width - 50*2, 180);
layer.cornerRadius=20;
layer.backgroundColor = [UIColor colorWithRed:0.9 green:0.8 blue:0.1 alpha:1].CGColor; [self.containerView.layer addSublayer:layer];CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
basicAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(layer.position.x, ScreenH + layer.bounds.size.height/2.0)];
basicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(layer.position.x, layer.bounds.size.height/2.0 + 80)];
basicAnimation.duration=2.f;
basicAnimation.fillMode = kCAFillModeForwards;
basicAnimation.removedOnCompletion=NO;CABasicAnimation *basicAnimation1 = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
basicAnimation1.fromValue = (id)[UIColor colorWithRed:0.6 green:0.1 blue:0.5 alpha:1].CGColor;
basicAnimation1.toValue = (id)[UIColor colorWithRed:0.1 green:0.6 blue:0.7 alpha:1].CGColor;
basicAnimation1.duration=2.0f;
basicAnimation1.fillMode = kCAFillModeForwards;
basicAnimation1.removedOnCompletion=NO;CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
springAnimation.fromValue=@(0.95);
springAnimation.toValue=@(1);
springAnimation.mass=1;//质量越大,惯性越大
springAnimation.stiffness=50;//劲度系数,值越大,运动的越快 springAnimation.damping=10;//阻尼系数 值越大 停止的越快 springAnimation.initialVelocity=50;//初始速度
springAnimation.duration= springAnimation.settlingDuration; springAnimation.beginTime=2.3f;
springAnimation.fillMode = kCAFillModeForwards;
springAnimation.removedOnCompletion=NO;CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.duration= springAnimation.settlingDuration+2.0f;
//这个里面的动画,没有先后顺序,可以设置beginTime动画组里的动画有先后顺序
groupAnimation.animations=@[springAnimation, basicAnimation,basicAnimation1];
groupAnimation.fillMode = kCAFillModeForwards; groupAnimation.removedOnCompletion=NO;
[layeraddAnimation:groupAnimationforKey:@"groupAnimation"];
}
组合动画效果图
CATransition
过渡动画/转场动画,比UIVIew转场动画具有更多的动画效果,比如Nav的默认Push视图的效果就是通过CATransition的kCATransitionPush类型来实现。
重要属性如下
startProgress: 动画的开始位置
endProgress: 动画的结束位置
type: 动画的类型
type的enum值如下:
kCATransitionFade 渐变
kCATransitionMoveIn 覆盖
kCATransitionPush 推出
kCATransitionReveal 揭开
subtype: 动画的方向
subtype的enum值如下:
kCATransitionFromRight 从右边
kCATransitionFromLeft 从左边
kCATransitionFromTop 从顶部
kCATransitionFromBottom 从底部
CATransition 实现两张图片渐变效果:
- (void)imageTransition {
UIImageView *img1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"meinv1.jpeg"]];
img1.frame = CGRectMake(0, 50, self.containerView.bounds.size.width, (self.containerView.bounds.size.height - 100));
img1.contentMode = UIViewContentModeScaleAspectFill; [self.containerViewaddSubview:img1];UIImageView *img2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"meinv2.jpeg"]];
img2.frame = CGRectMake(0, 50, self.containerView.bounds.size.width, (self.containerView.bounds.size.height - 100));
img2.contentMode = UIViewContentModeScaleAspectFill; [self.containerViewaddSubview:img2];dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CATransition *transition = [CATransition animation];
transition.startProgress=0.0f;
transition.endProgress=1.0f;
transition.duration=1.5f;
transition.type=kCATransitionFade;
transition.subtype = kCATransitionFromLeft;
transition.fillMode = kCAFillModeBackwards;//向两个图层添加动画
[img1.layeraddAnimation:transitionforKey:@"transition"]; [img2.layeraddAnimation:transitionforKey:@"transition"];//最后更改两个图层的可见性
img1.hidden=NO;
img2.hidden=YES;
});
}
两张图片渐变效果图:
后记
此致,core Animation 动画的基本内容全部说完了,想要掌握好核心动画,必须对CAAnimation的所有类和属性熟练掌握。再者就是需要结合实际场景把核心动画用起来,多练,多想,这样才能从简单的demo升级到可以做更多的炫酷效果。