CoreAnimation笔记

总结一下Core Animation的使用,本文主要借鉴了这篇博客
博客地址

1.简介

iOS动画主要是指Core Animation框架。
官方使用文档地址为Core Animation Guide
Core Animation是iOS和macOS平台上负责图形渲染与动画的基础框架。Core Animation可以作用与动画视图或者其他可视元素,为你完成了动画所需的大部分绘帧工作。你只需要配置少量的动画参数(如开始点的位置和结束点的位置)即可使用Core Animation的动画效果。Core Animation将大部分实际的绘图任务交给了图形硬件来处理,图形硬件会加速图形渲染的速度。这种自动化的图形加速技术让动画拥有更高的帧率并且显示效果更加平滑,不会加重CPU的负担而影响程序的运行速度。

2.Core Animation动画的使用

1.基础动画(CABaseAnimation)

重要属性
fromValue : keyPath对应的初始值
toValue : keyPath对应的结束值

基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、透明度、缩放、旋转、背景色等等。
效果演示

Drawing

位移动画代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//使用CABasicAnimation创建基础动画
//图层的position属性是一个CGPoint的值,它指定图层相当于它父图层的位置,该值基于父图层的坐标系。
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];

//fromValue : keyPath对应的初始值
basicAnimation.fromValue = [NSValue valueWithCGPoint:_testView.center];

//toValue : keyPath对应的结束值
basicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, _testView.center.y)];

//duration : 动画的持续时间
basicAnimation.duration = 1.0f;

//如果fillMode=kCAFillModeForwards并且removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。
// basicAnimation.fillMode = kCAFillModeForwards;
// basicAnimation.removedOnCompletion = NO;

/*timingFunction : 控制动画的显示节奏系统提供五种值选择,分别是:
*kCAMediaTimingFunctionLinear 线性动画
*kCAMediaTimingFunctionEaseIn 先慢后快
*kCAMediaTimingFunctionEaseOut 先快后慢
*kCAMediaTimingFunctionEaseInEaseOut 先慢后快再慢
*kCAMediaTimingFunctionDefault 默认,也属于中间比较快
*/
basicAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[_testView.layer addAnimation:basicAnimation forKey:@"positionAnimation"];

2.关键帧动画(CAKeyframeAnimation)

CAKeyframeAnimation和CABaseAnimation都属于CAPropertyAnimatin的子类。

CABaseAnimation只能从一个数值(fromValue)变换成另一个数值(toValue),而CAKeyframeAnimation则会使用一个NSArray保存一组关键帧。

重要属性

values : 就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧

path : 可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略。

keyTimes : 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的。

效果演示:

Drawing

圆形路径动画代码演示:

1
2
3
4
5
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
anima.path = path.CGPath;
anima.duration = 2.0f;
[_testView.layer addAnimation:anima forKey:@"pathAnimation"];

说明:CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation

3.组动画(CAAnimationGroup)

CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。

重要属性

animations : 用来保存一组动画对象的NSArray

效果演示:

Drawing

组动画代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//位移动画
CAKeyframeAnimation *anima1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
anima1.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];

//缩放动画
CABasicAnimation *anima2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anima2.fromValue = [NSNumber numberWithFloat:0.8f];
anima2.toValue = [NSNumber numberWithFloat:2.0f];

//旋转动画
CABasicAnimation *anima3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
anima3.toValue = [NSNumber numberWithFloat:M_PI*4];

//组动画
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = [NSArray arrayWithObjects:anima1,anima2,anima3, nil];
groupAnimation.duration = 4.0f;


[_testView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];

4.过渡动画(CATransition)

CAAnimation的子类,用于做过渡动画或者转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。

重要属性

type:动画过渡类型

Apple 官方的SDK其实只提供了四种过渡效果。

kCATransitionFade 渐变效果

kCATransitionMoveIn 进入覆盖效果

kCATransitionPush 推出效果

kCATransitionReveal 揭露离开效果

私有API提供了其他很多非常炫的过渡动画,比如@”cube”、@”suckEffect”、@”oglFlip”、 @”rippleEffect”、@”pageCurl”、@”pageUnCurl”、@”cameraIrisHollowOpen”、@”cameraIrisHollowClose”等。

注意点
私有api,不建议开发者们使用。因为苹果公司不提供维护,并且有可能造成你的app审核不通过。

subtype:动画过渡方向

kCATransitionFromRight 从右侧进入

kCATransitionFromLeft 从左侧进入

kCATransitionFromTop 从顶部进入

kCATransitionFromBottom 从底部进入

startProgress:动画起点(在整体动画的百分比)

endProgress:动画终点(在整体动画的百分比)

效果演示:

Drawing

5.仿射变换(AffineTransform)

效果演示:

Drawing

位移代码示例:

1
2
3
4
5
6
7
8
9
//CGAffineTransformIdentity是系统提供的一个常量,/* The identity transform: [ 1 0 0 1 0 0 ]. */(和原图一样的transform);特殊地,transform属性默认值为CGAffineTransformIdentity,可以在形变之后设置该值以还原到最初状态

_demoView.transform = CGAffineTransformIdentity;
[UIView animateWithDuration:1.0f animations:^{
//CGAffineTransformMakeTranslation实现以初始位置为基准,在x轴方向上平移x单位,在y轴方向上平移y单位
// 格式 CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
//注: 当tx为正值时,会向x轴正方向平移,反之,则向x轴负方向平移;当ty为正值时,会向y轴正方向平移,反之,则向y轴负方向平移
_demoView.transform = CGAffineTransformMakeTranslation(100, 100);
}];

6.综合案例

效果演示:

Drawing

1.仿Path菜单效果

动画解析:
1、点击红色按钮,红色按钮旋转。(旋转动画)

2、黑色小按钮依次弹出,并且带有旋转效果。(位移动画、旋转动画、组动画)

3、点击黑色小按钮,其他按钮消失,被点击的黑色按钮变大变淡消失。(缩放动画、alpha动画、组动画)

博主的话:代码过多,这里不做演示。文章最后提供代码下载地址。

2.仿钉钉菜单效果

看上去挺炫的,其实实现很简单,就是位移动画+缩放动画。

3.点赞烟花效果动画

这里其实只有按钮变大效果使用的缩放动画。烟花效果 使用的是一种比较特殊的动画–粒子动画。

一个粒子系统一般有两部分组成:

1、CAEmitterCell:可以看作是单个粒子的原型(例如,一个单一的粉扑在一团烟雾)。当散发出一个粒子,UIKit根据这个发射粒子和定义的基础上创建一个随机粒子。此原型包括一些属性来控制粒子的图片,颜色,方向,运动,缩放比例和生命周期。

2、CAEmitterLayer:主要控制发射源的位置、尺寸、发射模式、发射源的形状等等。

以上两个类的属性还是比较多的,这里就不细讲了。大家可以google一下,详细的了解吧。

7.波浪效果

效果演示:

Drawing

主要是通过正弦函数的算法去画两条错位的线,然后通过CADisplayLink去实时刷新屏幕

正弦函数算法解析

正弦型函数解析式:y=Asin(ωx+φ)+h

各常数值对函数图像的影响:

φ(初相位):决定波形与X轴位置关系或横向移动距离(左加右减)

ω:决定周期(最小正周期T=2π/|ω|)

A:决定峰值(即纵向拉伸压缩的倍数)

h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)*/

1、我们的容器高度是100,我希望波的整体高度,固定在容器的一个相对的位置。这里设置h = 30;也就是说,当Asin(ωx+φ)计算为0的时候,这个时候y的位置是30;

2、决定波起伏的高度,我们设置波峰是5,波峰越大,曲线越陡峭;

3、决定波的宽度和周期,比如,我们可以看到上面的例子中是一个周期的波曲线,一个波峰、一个波谷,如果我们想在0到2π这个距离显示2个完整的波曲线,那么周期就是π。我们这里设置波的宽度是容器的宽度_waveWidth,希望能展示2.5个波曲线,周期就是_waveWidth/2.5。那么ω常量就可以这样计算:2.5*M_PI/_waveWidth。

4、一共有两个波曲线,形成一个落差,也就是设置不同的φ(初相位),我们这里设置落差是M_PI/4。

5、时间和初相位的函数关系:我们在计时器的函数中一直调用_offset += _speed;可以看到,如果我们设置波的速度speed越大,波的震动将会越快。
最后我们的公式如下:

CGFloat y = _waveHeight*sinf(2.5*M_PI*i/_waveWidth + 3*_offset*M_PI/_waveWidth + M_PI/4) + _h;

这些参数都可以自己调整,得到一个符合要求的效果。

具体代码在文章最后的Demo里面。

8.自定义过渡动画

效果演示:

第一次演示的图片是个美女,居然被图片托管平台给和谐了,尴尬!!!!
我用的是图片托管平台是图片托管平台地址

Drawing

要点:

1.我们需要自定义一个遵循的<UIViewControllerAnimatedTransitioning>协议的动画过渡管理对象,并实现两个必须实现的方法:

1
2
3
4
//返回动画事件  
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
//所有的过渡动画事务都在这个方法里面完成
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

2.我们还需要自定义一个继承于UIPercentDrivenInteractiveTransition的手势过渡管理对象,我把它成为百分比手势过渡管理对象,因为动画的过程是通过百分比控制的

3.成为相应的代理,实现相应的代理方法,返回我们前两步自定义的对象就OK了 !

模态推送需要实现如下4个代理方法

1
2
3
4
5
6
7
8
//返回一个管理prenent动画过渡的对象
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//返回一个管理pop动画过渡的对象
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
//返回一个管理prenent手势过渡的对象
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
//返回一个管理pop动画过渡的对象
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;

导航控制器实现如下2个代理方法

1
2
3
4
5
6
7
8
//返回转场动画过渡管理对象
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);
//返回手势过渡管理对象
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);

我所实现的这个效果大概用了以上的那么多方法,具体还有很多你们可以在看过demo后自行发掘,很好玩。

9.Demo地址

Demo地址