一、概述
Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。它可以用在Mac OS X和iOS平台。Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。要注意的是,Core Animation是直接作用在CALayer上的,并非UIView。
二、Core Animation的继承结构
CAAnimation是所有动画对象的父类,负责控制动画的持续时间和速度、是个抽象类,不能直接使用,应该使用具体子类。需要注意的是CAAnimation 和 CAPropertyAnimation 都是抽象类。
view是负责响应事件的,layer是负责显示的。
黄色的区块是常用的属性、方法或者需要遵守的协议,灰色的是名称。
图注:
- 核心动画中所有类都遵守CAMediaTiming。
- CAAnaimation是个抽象类,不具备动画效果,必须用它的子类(CAAnimationGroup和CATransition)才有动画效果。
- CAAnimationGroup(动画组),可以同时进行缩放,旋转。
- CATransition(转场动画),界面之间跳转都可以用转场动画。
- CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类(CABasicAnimation和CAKeyframeAnimation)才有动画效果。
- CABasicAnimation(基础动画),做一些简单效果。
- CAKeyframeAnimation(帧动画),做一些连续的流畅的动画。
三、Core Animation使用步骤及相关属性
1、使用步骤
第一步:初始化一个CAAnimation对象,并设置一些动画相关属性。
第二步:通过调用CALayer的addAnimation:forKey:方法增加CAAnimation对象到CALayer中,这样就能开始执行动画了。
第三步:通过调用CALayer的removeAnimationForKey:方法可以停止CALayer中的动画。
2、常用属性
就是上面图片中的小黄色区内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
duration:持续时间,默认值是
0.25秒
repeatCount
:重复次数,无线循环可以设置
HUGE
_VALF或者
CGFLOAT_MAX
repeatDuration
:重复时间
removeOnCompletion
:
默认为
YES,代表动画执行完毕后就从图层上移除,图形会恢复到执行动画之前的状态。如果想要图层保持显示动画执行后的状态,那就设置为
NO,同时设置
fillMode为
kCAFillModeForwards
fillMode:决定当前对象在非
active时间段的行为
beginTime:可以用来设置动画延时执行,若想延迟
2s,就设置为
CACurrentMediaTIme
(
)
+
2
CACurrentMediaTIme
(
):图层的当前时间
timingFunction
:速度控制函数,控制动画运行节奏
delegate:动画代理
|
动画代理能够检测动画的执行和结束:
1
2
3
4
|
@
interface
NSObject
(
CAAnimationDelegate
)
-
(
void
)
animationDidStart
:
(
CAAnimation *
)
anim
;
-
(
void
)
animationDidStop
:
(
CAAnimation *
)
anim
finished
:
(
BOOL
)
flag
;
@
end
|
3、animationWithKeyPath中,常用的keyPath
4、动画填充模式
1
2
3
4
5
6
7
|
kCAFillModeForwards
:当动画结束后,
layer会一直保持着动画最后的状态
kCAFillModeBackwards:在动画开始前,只需要将动画加入了一个
layer,
layer便立即进入动画的初始状态并等待动画开始
kCAFillModeBoth:这个其实就是上面两个合成,动画加入后,开始之前,
layer便处于动画初始状态,动画结束后
layer保持动画最后的状态
kCAFillModeRemoved:这个是默认值,也就是说当动画开始前和动画结束后,动画对
layer都没有影响,动画结束后,
layer会恢复到之前的状态
|
5、动画速度控制函数
1
2
3
4
5
6
7
|
kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
|
6、CALayer上动画的暂停和恢复
1
2
3
4
5
6
7
8
9
10
|
#pragma mark 暂停CALayer的动画
-
(
void
)
pauseLayer
:
(
CALayer*
)
layer
{
CFTimeInterval
pausedTime
=
[
layer
convertTime
:
CACurrentMediaTime
(
)
fromLayer
:
nil
]
;
// 让CALayer的时间停止走动
layer
.
speed
=
0.0
;
// 让CALayer的时间停留在pausedTime这个时刻
layer
.
timeOffset
=
pausedTime
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#pragma mark 恢复CALayer的动画
-
(
void
)
resumeLayer
:
(
CALayer*
)
layer
{
CFTimeInterval
pausedTime
=
layer
.
timeOffset
;
// 1. 让CALayer的时间继续行走
layer
.
speed
=
1.0
;
// 2. 取消上次记录的停留时刻
layer
.
timeOffset
=
0.0
;
// 3. 取消上次设置的时间
layer
.
beginTime
=
0.0
;
// 4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
CFTimeInterval
timeSincePause
=
[
layer
convertTime
:
CACurrentMediaTime
(
)
fromLayer
:
nil
]
-
pausedTime
;
// 5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
layer
.
beginTime
=
timeSincePause
;
}
|
四、动画的调用方式
(1)UIView 代码块调用:
1
2
3
4
5
6
7
|
_demoView
.
frame
=
CGRectMake
(
0
,
SCREEN_HEIGHT
/
2
-
50
,
50
,
50
)
;
[
UIView
animateWithDuration
:
1.0f
animations
:
^
{
_demoView
.
frame
=
CGRectMake
(
SCREEN_WIDTH
,
SCREEN_HEIGHT
/
2
-
50
,
50
,
50
)
;
}
completion
:
^
(
BOOL
finished
)
{
_demoView
.
frame
=
CGRectMake
(
SCREEN_WIDTH
/
2
-
25
,
SCREEN_HEIGHT
/
2
-
50
,
50
,
50
)
;
}
]
;
|
(2)UIView [begin commit]模式
1
2
3
4
5
|
_demoView
.
frame
=
CGRectMake
(
0
,
SCREEN_HEIGHT
/
2
-
50
,
50
,
50
)
;
[
UIView
beginAnimations
:
nil
context
:
nil
]
;
[
UIView
setAnimationDuration
:
1.0f
]
;
_demoView
.
frame
=
CGRectMake
(
SCREEN_WIDTH
,
SCREEN_HEIGHT
/
2
-
50
,
50
,
50
)
;
[
UIView
commitAnimations
]
;
|
(3)使用Core Animation中的类
1
2
3
4
5
|
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"position"
]
;
anima
.
fromValue
=
[
NSValue
valueWithCGPoint
:
CGPointMake
(
0
,
SCREEN_HEIGHT
/
2
-
75
)
]
;
anima
.
toValue
=
[
NSValue
valueWithCGPoint
:
CGPointMake
(
SCREEN_WIDTH
,
SCREEN_HEIGHT
/
2
-
75
)
]
;
anima
.
duration
=
1.0f
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"positionAnimation"
]
;
|
addanimation:forkey: 这个key到底是什么?官方的注释是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/** Animation methods. **/
/* Attach an animation object to the layer. Typically this is implicitly
* invoked through an action that is an CAAnimation object.
*
* 'key' may be any string such that only one animation per unique key
* is added per layer. The special key 'transition' is automatically
* used for transition animations. The nil pointer is also a valid key.
*
* If the `duration' property of the animation is zero or negative it
* is given the default duration, either the value of the
* `animationDuration' transaction property or .25 seconds otherwise.
*
* The animation is copied before being added to the layer, so any
* subsequent modifications to `anim' will have no affect unless it is
* added to another layer. */
|
翻译下:
1
2
3
4
5
6
7
8
9
10
|
/
*
*动画方法。
*
*
//
*
将动画对象附加到层。通常这是隐式地
*
通过一个动作是一个
CAAnimation对象调用。
*
“
key”可以是任何字符串
,这样每个唯一键只有一个动画
*
添加每层。特殊键自动“过渡”
*
用于过渡动画。空指针也是一个有效的关键。
*
如果动画的“持续时间”属性是零或消极
*
默认时间
,给出的值
*
animationDuration事务属性或。
25秒。
*
动画复制之前添加到层
,所以任何后续修改“动画”没有影响
,除非它是添加到另一层
|
也就是说,key可以设置为任意值。
五、基本使用示例
1、基础动画(CABaseAnimation)
重要属性:
1
2
3
|
fromValue
:
keyPath对应的初始值
toValue
:
keyPath对应的结束值
|
基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、透明度、缩放、旋转、背景色等等。
例如:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
//
// ViewController.m
// AnimationTest
//
// Created by 李峰峰 on 2017/2/11.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import "ViewController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@
interface
ViewController
(
)
@
property
(
nonatomic
,
strong
)
UIView *
myView
;
@
end
@
implementation
ViewController
-
(
void
)
viewDidLoad
{
[
super
viewDidLoad
]
;
CGFloat
myViewW
=
100
;
CGFloat
myViewH
=
100
;
CGFloat
myViewX
=
(
SCREEN_WIDTH
-
myViewW
)
/
2
;
CGFloat
myViewY
=
(
SCREEN_HEIGHT
-
myViewH
)
/
2
;
self
.
myView
=
[
[
UIView
alloc
]
initWithFrame
:
CGRectMake
(
myViewX
,
myViewY
,
myViewW
,
myViewH
)
]
;
[
self
.
view
addSubview
:
self
.
myView
]
;
self
.
myView
.
backgroundColor
=
[
UIColor
redColor
]
;
}
-
(
void
)
touchesBegan
:
(
NSSet
<
UITouch *
>
*
)
touches
withEvent
:
(
UIEvent *
)
event
{
//使用CABasicAnimation创建基础动画
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"position"
]
;
anima
.
fromValue
=
[
NSValue
valueWithCGPoint
:
CGPointMake
(
0
,
SCREEN_HEIGHT
/
2
-
75
)
]
;
anima
.
toValue
=
[
NSValue
valueWithCGPoint
:
CGPointMake
(
SCREEN_WIDTH
,
SCREEN_HEIGHT
/
2
-
75
)
]
;
anima
.
duration
=
1.0f
;
//anima.fillMode = kCAFillModeForwards;
//anima.removedOnCompletion = NO;
[
self
.
myView
.
layer
addAnimation
:
anima
forKey
:
@
"positionAnimation"
]
;
}
@
end
|
运行结果:
其他一些动画效果:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/**
* 透明度动画
*/
-
(
void
)
opacityAniamtion
{
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"opacity"
]
;
anima
.
fromValue
=
[
NSNumber
numberWithFloat
:
1.0f
]
;
anima
.
toValue
=
[
NSNumber
numberWithFloat
:
0.2f
]
;
anima
.
duration
=
1.0f
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"opacityAniamtion"
]
;
}
/**
* 缩放动画
*/
-
(
void
)
scaleAnimation
{
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"transform.scale"
]
;
//同上
anima
.
toValue
=
[
NSNumber
numberWithFloat
:
2.0f
]
;
anima
.
duration
=
1.0f
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"scaleAnimation"
]
;
}
/**
* 旋转动画
*/
-
(
void
)
rotateAnimation
{
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"transform.rotation.z"
]
;
//绕着z轴为矢量,进行旋转(@"transform.rotation.z"==@@"transform.rotation")
anima
.
toValue
=
[
NSNumber
numberWithFloat
:
3
*
M_PI
]
;
anima
.
duration
=
1.0f
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"rotateAnimation"
]
;
}
/**
* 背景色变化动画
*/
-
(
void
)
backgroundAnimation
{
CABasicAnimation *
anima
=
[
CABasicAnimation
animationWithKeyPath
:
@
"backgroundColor"
]
;
anima
.
toValue
=
(
id
)
[
UIColor
greenColor
]
.
CGColor
;
anima
.
duration
=
1.0f
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"backgroundAnimation"
]
;
}
|
2、CAKeyframeAnimation 关键帧动画
Keyframe顾名思义就是关键点的frame,你可以通过设定CALayer的始点、中间关键点、终点的frame,时间,动画会沿你设定的轨迹进行移动 。
CAKeyframeAnimation 的一些比较重要的属性:
1
2
3
|
path:这是一个
CGPathRef
对象,默认是空的,当我们创建好
CAKeyframeAnimation的实例的时候,可以通过制定一个自己定义的
path来让某一个物体按照这个路径进行动画。这个值默认是
nil,当其被设定的时候,
values
这个属性就被覆盖。
values:一个数组,提供了一组关键帧的值,当使用
path的
时候
values的值自动被忽略。
|
例如,连续移动一个view到不同的位置:
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
27
28
|
/**
* 关键帧动画 values
*/
-
(
void
)
keyFrameAnimation
{
CAKeyframeAnimation *
anima
=
[
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
)
]
;
anima
.
values
=
[
NSArray
arrayWithObjects
:
value0
,
value1
,
value2
,
value3
,
value4
,
value5
,
nil
]
;
anima
.
duration
=
2.0f
;
anima
.
timingFunction
=
[
CAMediaTimingFunction
functionWithName
:
kCAMediaTimingFunctionEaseOut
]
;
//设置动画的节奏
anima
.
delegate
=
self
;
//设置代理,可以检测动画的开始和结束
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"keyFrameAnimation"
]
;
}
/**
* path动画
*/
-
(
void
)
pathAnimation
{
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
;
[
_demoView
.
layer
addAnimation
:
anima
forKey
:
@
"pathAnimation"
]
;
}
|
运行效果就不贴出来了,大家可以自己尝试下。
对比基础动画跟关键帧动画,关键帧动画引入了动画占比时长的概念,这让我们能控制每个关键帧动画的占用比例而不是传入一个无意义的动画时长 —— 这让我们的代码更加难以理解。当然,除了动画占比之外,关键帧动画的options参数也让动画变得更加平滑,下面是关键帧特有的配置参数:
1
2
3
4
5
6
7
8
9
|
UIViewKeyframeAnimationOptionCalculationModeLinear
// 连续运算模式,线性
UIViewKeyframeAnimationOptionCalculationModeDiscrete
// 离散运算模式,只显示关键帧
UIViewKeyframeAnimationOptionCalculationModePaced
// 均匀执行运算模式,线性
UIViewKeyframeAnimationOptionCalculationModeCubic
// 平滑运算模式
UIViewKeyframeAnimationOptionCalculationModeCubicPaced
// 平滑均匀运算模式
|
3、CAAnimationGroup 组合动画
Group也就是组合的意思,可以保存一组动画对象,将CAAnimationGroup对象加入图层后,组中所有动画对象可以同时并发运行。
其重要属性为:
1
|
animations:用来保存一组动画对象的
NSArray
|
注意:默认情况下,一组动画对象是同时运行的,我们也可以通过设置动画对象的beginTime属性来更改动画的开始时间。
如下:
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
27
|
-
(
void
)
groupAnimation1
{
// 位移动画
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
;
[
_demoView
.
layer
addAnimation
:
groupAnimation
forKey
:
@
"groupAnimation"
]
;
}
|
运行效果:
4、CATransition 过渡动画
CAAnimation的子类,用于做过渡动画或者转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。
重要属性:
1
2
3
4
5
6
7
|
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点
(在整体动画的百分比
)
endProgress:动画终点
(在整体动画的百分比
)
|
有以下几种type:
有以下几种subtype:
1
2
3
4
|
kCATransitionFromRight
从右侧进入
kCATransitionFromLeft
从左侧进入
kCATransitionFromTop
从顶部进入
kCATransitionFromBottom
从底部进入
|
例如,为了便于使用,我们可以自定义一个相关方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#pragma CATransition动画实现
-
(
void
)
transitionWithType
:
(
NSString *
)
type
WithSubtype
:
(
NSString *
)
subtype
ForView
:
(
UIView *
)
view
{
//创建CATransition对象
CATransition *
animation
=
[
CATransition
animation
]
;
//设置运动时间
animation
.
duration
=
DURATION
;
//设置运动type
animation
.
type
=
type
;
if
(
subtype
!=
nil
)
{
//设置子类
animation
.
subtype
=
subtype
;
}
//设置运动速度
animation
.
timingFunction
=
UIViewAnimationOptionCurveEaseInOut
;
[
view
.
layer
addAnimation
:
animation
forKey
:
@
"animation"
]
;
}
|
除了上面layer的动画之外,还有UIView的动画:
例如:
1
2
3
4
5
6
|
[
UIView
beginAnimations
:
@
"animationID"
context
:
nil
]
;
[
UIView
setAnimationDuration
:
0.5f
]
;
//动画时长
[
UIView
setAnimationCurve
:
UIViewAnimationCurveEaseInOut
]
;
[
UIView
setAnimationTransition
:
UIViewAnimationTransitionFlipFromLeft
forView
:
self
.
myView
cache
:
YES
]
;
//给视图添加过渡效果
//在这里写你的代码.
[
UIView
commitAnimations
]
;
//提交动画
|
运行效果:
以上就是Core Animation的主要用法。