移动开发(IOS) – 动画
1.CALayer
1.1.学习核心动画之前,需要先理解 CALayer,因为核心动画操作的对象不是 UIView,而是 CALayer 。
1.2.CALayer 是核心动画的基础,可以做圆角、阴影、边框等效果 。
1.3.每个 UIView 内部都有一个 Layer 的属性 。
1.4.在实现核心动画时,本质上是将 CALayer 中的内容转换成位图,从而便于图形硬件的操纵 。
1.5.在 UIView 中 CALayer 只是一个类声明,因此需要添加 QuartzCore 框架 。
1.6.UIKit 框架只能应用在 iOS 而不能用于 Mac,但是 Quartz 2D 是可以跨平台的,因此在使用颜色时,不能直接使用 UIColor而需要将颜色转成 CGColor 。
1.7.修改图层相当于修改UIView属性,即修改了界面属性 。
1.8.形变属性既可以用形变函数指定,也可以用keyPath指定 。
1.9.创建视图对象时,视图会自己创建一个层,视图在绘图(如 drawRect: )时,会将内容画在自己的层上。当视图在层上完成绘图后,系统会将图层拷贝至屏幕。每个视图都有一个层,而每个图层又可以有多个子层 。
1.10.Layer 的设计目的不是为了取代视图,因此不能基于 CALayer 创建一个独立的可视化组件 。
1.11.Layer 的设计目的是提供视图的基本可视内容,从而提高动画的执行效率 。
1.12.除提供可视内容外,Layer 不负责视图的事件响应、内容绘制等工作,同时 Layer 不能参与到响应者链条中 。
1.13.CALayer 层次结构:
1.14.CALayer 的使用说明
1.14.1.通过 UIView 的 layer 属性可以拿到对应的根层,这个层不允许重新创建,但可以往层里面添加子层(调用 CALayer 的 addSublayer )。
1.14.2.要具体使用CALayer,需要引入<QuartzCore/QuartzCore.h>。
1.14.3.获取当前图层或使用静态方法 layer 初始化 CALayer 后,可以设置以下属性:
bounds | 宽度和高度 |
position | 位置(默认指中心点,具体由 anchorPoint 决定) |
anchorPoint | 锚点( x,y 的范围都是 0-1 ),决定了 position 的含义 |
backgroundColor | 背景颜色( CGColorRef 类型) |
borderColor | 边框颜色( CGColorRef 类型) |
borderWidth | 边框宽度 |
cornerRadius | 圆角半径 |
contents | 内容(比如设置为图片 CGImageRef ) |
transform | 旋转、缩放、平移 |
1.14.4.虽然 CALayer 可以使用 frame,但最好还是使用 bounds 和 position。为层设置动画时,用 bounds 和 position 会方便一点。
1.14.5.注意锚点和位置的关系,以及在旋转转换时对图层的影响。
1.14.6.UIView 有一个 addSubview 方法,而 layer 有一个 addSubLayer 方法。
1.14.7.CALayer 中使用 CGColorRef 和 CGImageRef 的数据类型,而不用 UIColor 和 UIImage 。
1.14.8.可以通过 UIKi t对象的特定方法,可以得到 Core Graphics 对象,如 UIImage 的 CGImage 方法和 UIColor 的 CGColor 方法。
1.15.CALayer 的隐式动画属性
1.15.1.每一个 UIView 内部都默认关联着一个 CALayer,称这个 Layer 为 Root Layer。所有的非 Root Layer 都存在着隐式动画,隐式动画的默认时长为 1/4 秒。
1.15.2.当修改非 Root Layer 的部分属性时,相应的修改会自动产生动画效果,能执行隐式动画的属性被称为“可动画属性”,诸如:
bounds | 缩放动画 |
position | 平移动画 |
opacity | 淡入淡出动画(改变透明度) |
… … | … … |
1.15.3.如果要关闭默认的动画效果,可以通过动画事务方法实现:
1
2
3
4
|
[CATransaction begin];
[CATransaction setDisableActions:
YES
];
// ...
[CATransaction commit];
|
1.16.在 CALayer 上绘图
1.16.1.创建一个 CALayer 的子类,然后覆盖 drawInContext: 方法,可以使用 Quartz2D API 在其中进行绘图。
1.16.2.设置 CALayer 的 delegate,然后让 delegate 实现 drawLayer:inContext: 方法进行绘图。
1.16.3.不能再将 UIView 设置为这个 CALayer 的 delegate,因为 UIView 对象已经是内部层的 delegate,再次设置会出问题。
1.16.4.无论使用哪种方法,都必须向层发送 setNeedsDisplay 消息,以触发相应绘图方法的调用。
1.17.CALayer、 UIView 以及上下文之间的关系:
1.17.1.当 UIView 收到 setNeedsDisplay 消息时,CALayer 会准备好一个 CGContextRef,然后向它的 delegate 即 UIView,发送消息,并且传入已经准备好的 CGContextRef 对象。UIView 在 drawLayer:inContext: 方法中会调用自己的 drawRect: 方法。
1.17.2.平时在 drawRect: 中通过 UIGraphicsGetCurrentContext() 获取的就是由 CALayer 传入的 CGContextRef 对象,在 drawRect: 中完成的所有绘图都会填入 CALayer 的 CGContextRef 中,然后被拷贝至屏幕。
1.17.3. CALayer 的 CGContextRef 用的是位图上下文( Bitmap Graphics Context )。
1.18.在实现核心动画时,本质上是将 CALayer 中的内容转换成位图,从而便于图形硬件的操纵。
2.Core Animation
2.1.Core Animation 是跨平台的,支持 iOS 环境和 Mac OS X 环境。
2.2.使用它需要先添加 QuartzCore.framework 和引入对应的框架 <QuartzCore/QuartzCore.h> 。
2.3.开发步骤:
2.3.1.初始化一个动画对象 ( CAAnimation ) 并设置一些动画相关属性。
2.3.2.CALayer 中很多属性都可以通过 CAAnimation 实现动画效果,包括:opacity、 position、 transform、 bounds、 contents 等( 可以在 API 文档中搜索: CALayer Animatable Properties )。
2.3.3.添加动画对象到层( CALayer )中,开始执行动画。
2.3.4.通过调用 CALayer 的 addAnimation:forKey 增加动画到层( CALayer )中,这样就能触发动画了。通过调用 removeAnimationForKey 可以停止层中的动画。
2.3.5.Core Animation 的动画执行过程都是在后台操作的,不会阻塞主线程。
3.CAAnimation
3.1.CAAnimation 继承结构
3.2.CAAnimation 是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类。
3.3.属性说明:
duration | (来自CAMediaTiming协议的属性)动画的持续时间 |
repeatCount | (来自CAMediaTiming协议的属性)重复次数,无限循环可以设置 HUGE_VALF 或者 MAXFLOAT |
repeatDuration | (来自CAMediaTiming协议的属性)重复时间 |
removedOnCompletion | 默认为 YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为 NO,不过还要设置 fillMode 为 kCAFillModeForwards |
fillMode | (来自CAMediaTiming协议的属性)决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之后 |
beginTime | (来自CAMediaTiming协议的属性)可以用来设置动画延迟执行时间,若想延迟 2s,就设置为 CACurrentMediaTime()+2 , CACurrentMediaTime() 为图层的当前时间 |
timingFunction | 速度控制函数,控制动画运行的节奏 |
delegate | 动画代理 |
3.4.CAAnimation 的动画填充模式,fillMode 属性值(要想 fillMode 有效,最好设置 removedOnCompletion = NO ):
3.4.1.kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对 layer 都没有影响,动画结束后,layer 会恢复到之前的状态。
3.4.2.kCAFillModeForwards 当动画结束后,layer 会一直保持着动画最后的状态 。
3.4.3.kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个 layer,layer 便立即进入动画的初始状态并等待动画开始。
3.4.4.kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer 便处于动画初始状态,动画结束后 layer 保持动画最后的状态。
3.5.CAAnimation 的速度控制函数( CAMediaTimingFunction ):
3.5.1. kCAMediaTimingFunctionLinear (线性):匀速,给你一个相对静态的感觉。
3.5.2. kCAMediaTimingFunctionEaseIn (渐进):动画缓慢进入,然后加速离开。
3.5.3. kCAMediaTimingFunctionEaseOut (渐出):动画全速进入,然后减速的到达目的地。
3.5.4. kCAMediaTimingFunctionEaseInEaseOut (渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
3.6.CAAnimation 动画代理方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@interface
NSObject
(CAAnimationDelegate)
/* Called when the animation begins its active duration. */
- (
void
)animationDidStart:(CAAnimation *)anim;
/* Called when the animation either completes its active duration or
* is removed from the object it is attached to (i.e. the layer). 'flag'
* is true if the animation reached the end of its active duration
* without being removed. */
- (
void
)animationDidStop:(CAAnimation *)anim finished:(
BOOL
)flag;
@end
|
3.7.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
|
#pragma mark 暂停CALayer的动画
-(
void
)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:
nil
];
// 让CALayer的时间停止走动
layer.speed = 0.0;
// 让CALayer的时间停留在pausedTime这个时刻
layer.timeOffset = pausedTime;
}
#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;
}
|
4.CAPropertyAnimation
4.1.CAPropertyAnimation 是 CAAnimation 的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类: CABasicAnimation, CAKeyframeAnimation 。
4.2.属性说明:
keyPath | 通过指定 CALayer 的一个属性名称为 keyPath ( NSString 类型),并且对 CALayer 的这个属性的值进行修改,达到相应的动画效果。比如,指定 @”position” 为 keyPath,就修改 CALayer 的 position 属性的值,以达到平移的动画效果。 |
5.CABasicAnimation
5.1.基本 CABasicAnimation,是 CAPropertyAnimation 的子类。
5.2.属性说明:
fromValue | keyPath 相应属性的初始值 |
toValue | keyPath 相应属性的结束值 |
5.3.动画过程说明:
5.3.1.随着动画的进行,在长度为 duration 的持续时间内,keyPath 相应属性的值从 fromValue 渐渐地变为 toValue 。
5.3.2.keyPath 内容是 CALayer 的可动画 Animatable 属性。
5.3.3.如果 fillMode = kCAFillModeForwards 同时 removedOnComletion = NO ,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变 。
5.3.4.如果只是实现简单属性变化的动画效果,可以使用 UIView 的 block 动画替代基本动画。
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
|
/***平移***/
// 1. 实例化动画
// 如果没有指定图层的锚点(定位点)postion对应UIView的中心点
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:
@"position"
];
// 2. 设置动画属性
// 1) fromValue(myView的当前坐标) & toValue
[anim setToValue:[
NSValue
valueWithCGPoint:point]];
// 2) 动画的时长
[anim setDuration:1.0f];
// 3) 设置代理
[anim setDelegate:
self
];
// 4) 让动画停留在目标位置
/*
*提示:通过设置动画在完成后不删除,以及向前填充,可以做到平移动画结束后,
*UIView看起来停留在目标位置,但是其本身的frame并不会发生变化
*/
[anim setRemovedOnCompletion:
NO
];
// forwards是逐渐逼近目标点
[anim setFillMode:kCAFillModeForwards];
// 5) 要修正坐标点的实际位置可以利用setValue方法
[anim setValue:[
NSValue
valueWithCGPoint:point] forKey:
@"targetPoint"
];
[anim setValue:
@"translationTo"
forKey:
@"animationType"
];
// 3. 将动画添加到图层
// 将动画添加到图层之后,系统会按照定义好的属性开始动画,通常程序员不在与动画进行交互
[
self
.myView.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/***代理方法***/
//动画开始(极少用)
- (
void
)animationDidStart:(CAAnimation *)anim
{
NSLog
(
@"开始动画"
);
}
//动画结束(通常在动画结束后,做动画的后续处理)
- (
void
)animationDidStop:(CAAnimation *)anim finished:(
BOOL
)flag
{
NSString
*type = [anim valueForKey:
@"animationType"
];
if
([type isEqualToString:
@"translationTo"
]) {
// 1. 通过键值取出需要移动到的目标点
CGPoint point = [[anim valueForKey:
@"targetPoint"
]CGPointValue];
NSLog
(
@"目标点: %@"
,
NSStringFromCGPoint
(point));
// 2. 设置myView的坐标点
[
self
.myView setCenter:point];
}
NSLog
(
@"结束动画,myView: %@"
,
NSStringFromCGRect
(
self
.myView.frame));
}
|
1
2
3
4
5
6
|
/***block 动画***/
[UIView animateWithDuration:1.0f animations:^{
[
self
.myView setCenter:location];
} completion:^(
BOOL
finished) {
NSLog
(
@"%@"
,
NSStringFromCGRect
(
self
.myView.frame));
}];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/***旋转动画***/
// 1. 实例化基本动画
// 默认按照z轴旋转
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:
@"transform.rotation.z"
];
[
self
.myView.layer setAnchorPoint:CGPointMake(0, 0)];
// 2. 设置动画属性
// 不停的旋转
// 1) 旋转一周
[anim setToValue:@(2 * M_PI)];
// 2) 不停的旋转 - 动画循环播放
// HUGE_VALF 是一个非常大得浮点数,指定此数值可以认为动画无限循环,这里不要使用 MAXFLOAT
[anim setRepeatCount:HUGE_VALF];
[anim setDuration:0.5f];
// 3) 动画完成时删除
// 对于循环播放的动画效果,一定要将 removedOnCompletion 设置为 NO,否则无法恢复动画
[anim setRemovedOnCompletion:
NO
];
// 3. 添加动画
// key 可以随便指定,用于判断图层中是否存在该动画
[
self
.myView.layer addAnimation:anim forKey:
@"rotationAnim"
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/***缩放动画***/
// 1. 实例化基本动画
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:
@"transform.scale"
];
// 2. 设置动画属性
// fromValue & toValue
[anim setFromValue:@(1.0)];
// 从当前大小缩小到一半,然后恢复初始大小
[anim setToValue:@(0.5)];
// 自动翻转动画
[anim setAutoreverses:
YES
];
// 动画时长
[anim setDuration:0.5f];
// 3. 将动画添加到图层
[
self
.myView.layer addAnimation:anim forKey:
nil
];
|
6.CAKeyframeAnimation
6.1.CAKeyframeAnimation ,也是 CAPropertyAnimation 的子类。
6.2.与 CABasicAnimation 的区别是:
6.2.1.CABasicAnimation 只能从一个数值( fromValue )变到另一个数值( toValue ),而 CAKeyframeAnimation 会使用一个 NSArray 保存这些数值 。
6.2.2.CABasicAnimation 可看做是只有2个关键帧的 CAKeyframeAnimation 。
6.3.常用属性:
values | 上述的NSArray对象。里面的元素称为”关键帧” ( keyframe )。动画对象会在指定的时间( duration )内,依次显示 values 数组中的每一个关键帧。 |
path | 可以设置一个 CGPathRef、 CGMutablePathRef ,让图层按照路径轨迹移动。path 只对 CALayer 的 anchorPoint 和position 起作用。如果设置了 path,那么 values 将被忽略。 |
keyTimes | 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes 中的每一个时间值都对应values中的每一帧。如果没有设置 keyTimes ,各个关键帧的时间是平分的。 |
1
2
3
4
5
6
7
8
9
10
11
|
/***晃动动画***/
//实例化关键帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:
@"transform.rotation"
];
[anim setDuration:0.5f];
//晃动角度
CGFloat angel = M_PI_4 / 12.0;
[anim setValues:@[@(angel), @(-angel), @(angel)]];
//设置循环晃动
[anim setRepeatCount:HUGE_VALF];
//将动画添加到图层
[
self
.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/***离散点平移动画***/
// 1. 实例化关键帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:
@"position"
];
// 2. 设置关键帧动画属性
NSValue
*p1 = [
NSValue
valueWithCGPoint:
self
.center];
NSValue
*p2 = [
NSValue
valueWithCGPoint:CGPointMake(0, 0)];
NSValue
*p3 = [
NSValue
valueWithCGPoint:point];
[anim setValues:@[p1, p2, p3]];
[anim setDuration:1.0f];
//设置键值记录目标位置,以便动画结束后,修正位置
[anim setValue:
@"translationTo"
forKey:
@"animationType"
];
[anim setValue:p3 forKey:
@"targetPoint"
];
//设置代理
[anim setDelegate:
self
];
// 3. 将动画添加到图层
[
self
.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/***路径平移动画***/
// 1. 实例化关键帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:
@"position"
];
[anim setDuration:duration];
// 2. 按照矩形移动,需要使用到路径
// 1) 创建路径
CGMutablePathRef path = CGPathCreateMutable();
// 2) 设置路径内容
// 起点,宽、高
CGFloat w = to.x -
self
.center.x;
CGFloat h = to.y -
self
.center.y;
CGRect rect = CGRectMake(
self
.center.x,
self
.center.y, w, h);
CGPathAddRect(path,
nil
, rect);
// 3) 将路径添加到动画
[anim setPath:path];
// 4) 释放路径
CGPathRelease(path);
// 3. 将动画添加到图层
[
self
.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/***贝塞尔曲线(一个控制点)动画***/
// 1. 实例化关键帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:
@"position"
];
[anim setDuration:duration];
// 2. 设置路径
// 中间的控制点使用屏幕上得随机点
CGPoint cp = [
self
randomPoint];
CGMutablePathRef path = CGPathCreateMutable();
// 设置起始点
CGPathMoveToPoint(path,
NULL
,
self
.center.x,
self
.center.y);
// 添加带一个控制点的贝塞尔曲线
CGPathAddQuadCurveToPoint(path,
NULL
, cp.x, cp.y, to.x, to.y);
[anim setPath:path];
CGPathRelease(path);
// 设置键值记录目标位置,以便动画结束后,修正位置
[anim setValue:
@"translationTo"
forKey:
@"animationType"
];
[anim setValue:[
NSValue
valueWithCGPoint:to] forKey:
@"targetPoint"
];
[anim setDelegate:
self
];
// 3. 将动画添加到图层
[
self
.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/***贝塞尔曲线(两个控制点)动画***/
// 1. 实例化关键帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:
@"position"
];
// 2. 设置路径
[anim setDuration:duration];
// 中间的控制点使用屏幕上得随机点
CGPoint cp1 = [
self
randomPoint];
CGPoint cp2 = [
self
randomPoint];
CGMutablePathRef path = CGPathCreateMutable();
// 设置起始点
CGPathMoveToPoint(path,
NULL
,
self
.center.x,
self
.center.y);
// 添加带一个控制点的贝塞尔曲线
CGPathAddCurveToPoint(path,
NULL
, cp1.x, cp1.y, cp2.x, cp2.y, to.x, to.y);
[anim setPath:path];
CGPathRelease(path);
// 设置键值记录目标位置,以便动画结束后,修正位置
[anim setValue:
@"translationTo"
forKey:
@"animationType"
];
[anim setValue:[
NSValue
valueWithCGPoint:to] forKey:
@"targetPoint"
];
[anim setDelegate:
self
];
// 3. 将动画添加到图层
[
self
.layer addAnimation:anim forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
|
/***动画代理方法***/
- (
void
)animationDidStop:(CAAnimation *)anim finished:(
BOOL
)flag
{
// 取出动画类型
NSString
*type = [anim valueForKey:
@"animationType"
];
if
([type isEqualToString:
@"translationTo"
]) {
// 取出目标点,并设置self.center
self
.center = [[anim valueForKey:
@"targetPoint"
]CGPointValue];
}
}
|
6.4.CAKeyframeAnimation 计算模式属性 (calculationMode)
6.4.1.所谓计算模式:其主要针对的是每一帧的内容为一个坐标点的情况,也就是对 anchorPoint 和 position 进行的动画。
6.4.2.当在平面坐标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算。
6.4.3.calculationMode目前提供如下几种模式:
kCAAnimationLinear | 默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算 |
kCAAnimationDiscrete | 离散的,不进行插值计算,所有关键帧直接逐个进行显示 |
kCAAnimationPaced | 使得动画均匀进行,而不是按 keyTimes 设置的或者按关键帧平分时间,此时 keyTimes 和 timingFunctions 无效 |
kCAAnimationCubic | 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑 |
kCAAnimationCubicPaced | 在 kCAAnimationCubic 的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时 keyTimes 以及 timingFunctions 也是无效的。 |
6.4.4.此属性研究的优先级不高,只有再做复杂动画,同时动画效果不理想的时候,才需要考虑使用这一属性。
7.CAAnimationGroup
7.1.CAAnimationGroup,是CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。
7.2.属性说明:
animations | 用来保存一组动画对象的 NSArray |
7.3.默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的 beginTime 属性来更改动画的开始时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/***动画组***/
// 1. 实例化动画组
CAAnimationGroup *group = [[CAAnimationGroup alloc]init];
CFTimeInterval duration = 2.0;
// 2. 定义动画组中的动画
CAKeyframeAnimation *move = [AnimationView moveWithDuration:duration from:
self
.myView.center to:location controlPointCount:4];
CABasicAnimation *rotation = [AnimationView rotationWithDuration:duration from:0.0 to:2 * M_PI];
CABasicAnimation *scale = [AnimationView scaleWithDuration:duration from:2.0 to:0.5];
CABasicAnimation *opacity = [AnimationView opacityWithDuration:duration from:0.1 to:1.0];
// 3. 将定义的动画添加到动画组
[group setAnimations:@[move, rotation, scale, opacity]];
// 定义动画组执行的时间长度
[group setDuration:duration];
// 5) 设置键值记录目标位置,以便动画结束后,修正位置
// 并不是所有的动画方法都需要设置目标点
[group setValue:
@"translationTo"
forKey:
@"animationType"
];
[group setValue:[
NSValue
valueWithCGPoint:location] forKey:
@"targetPoint"
];
[group setDelegate:
self
];
// 4. 将动画组添加到视图的图层
[
self
.myView.layer addAnimation:group forKey:
nil
];
|
1
2
3
4
5
6
7
8
9
10
|
/***动画代理方法***/
- (
void
)animationDidStop:(CAAnimation *)anim finished:(
BOOL
)flag
{
// 取出动画类型
NSString
*type = [anim valueForKey:
@"animationType"
];
if
([type isEqualToString:
@"translationTo"
]) {
// 取出目标点,并设置self.center
self
.center = [[anim valueForKey:
@"targetPoint"
]CGPointValue];
}
}
|
8.CATransition
8.1.CATransition 是 CAAnimation 的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。 iOS 比 Mac OS X 的转场动画效果少一点。
8.2.UINavigationController 就是通过 CATransition 实现了将控制器的视图推入屏幕的动画效果。
8.3.动画属性:
type | 动画过渡类型 |
subtype | 动画过渡方向 |
startProgress | 动画起点(在整体动画的百分比) |
endProgress | 动画终点(在整体动画的百分比) |
8.4.转场动画过渡效果:
类型字符串 | 效果说明 | 关键字 | 方向 |
fade | 交叉淡化过渡 | YES | |
push | 新视图把旧视图推出去 | YES | |
moveIn | 新视图移到旧视图上面 | YES | |
reveal | 将旧视图移开,显示下面的新视图 | YES | |
cube | 立方体翻滚效果 | ||
oglFlip | 上下左右翻转效果 | ||
suckEffect | 收缩效果,如一块布被抽走 | NO | |
rippleEffect | 水滴效果 | NO | |
pageCurl | 向上翻页效果 | ||
pageUnCurl | 向下翻页效果 | ||
cameraIrisHollowOpen | 相机镜头打开效果 | NO | |
cameraIrisHollowClose | 相机镜头关闭效果 | NO |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// 1. 实例化转场动画 注意不要和CATransaction(动画事务)搞混
CATransition *transition = [[CATransition alloc]init];
// 设置类型 type
[transition setType:
@"moveIn"
];
// 根据轻扫方向设置子类型 subType
// 2. 判断轻扫的方向
UIImageView *imageView = (UIImageView *)recognizer.view;
if
(UISwipeGestureRecognizerDirectionLeft == recognizer.direction) {
NSLog
(
@"向左"
);
[transition setSubtype:kCATransitionFromRight];
imageView.tag = (imageView.tag + 1) %
self
.imageList.count;
}
else
{
NSLog
(
@"向右"
);
[transition setSubtype:kCATransitionFromLeft];
// 针对负数去模,需要注意修正索引
imageView.tag = (imageView.tag - 1 +
self
.imageList.count) %
self
.imageList.count;
}
[transition setDuration:0.5f];
[imageView setImage:
self
.imageList[imageView.tag]];
// 3. 动画添加到图层
[recognizer.view.layer addAnimation:transition forKey:
nil
];
|
8.5.使用UIView动画函数实现转场动画(单视图):
1
2
3
4
5
6
7
8
|
/*
* duration:动画的持续时间
* view:需要进行转场动画的视图
* options:转场动画的类型
* animations:将改变视图属性的代码放在这个block中
* completion:动画结束后,会自动调用这个block
*/
+ (
void
)transitionWithView:(UIView *)view duration:(
NSTimeInterval
)duration options:(UIViewAnimationOptions)options animations:(
void
(^)(
void
))animations completion:(
void
(^)(
BOOL
finished))completion;
|
1
2
3
4
5
6
7
8
|
/***单视图(翻转转场动画)***/
[UIView transitionWithView:
self
.imageView duration:1.0f options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
// 在此设置视图反转之后显示的内容
self
.imageView.tag = (
self
.imageView.tag + 1) %
self
.imageList.count;
[
self
.imageView setImage:
self
.imageList[
self
.imageView.tag]];
} completion:^(
BOOL
finished) {
NSLog
(
@"翻转完成"
);
}];
|
8.6.使用 UIView 动画函数实现转场动画(双视图):
1
2
3
4
5
6
7
|
/*
* duration:动画的持续时间
* options:转场动画的类型
* animations:将改变视图属性的代码放在这个block中
* completion:动画结束后,会自动调用这个block
*/
+ (
void
)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(
NSTimeInterval
)duration options:(UIViewAnimationOptions)options completion:(
void
(^)(
BOOL
finished))completion;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/***双视图***/
// 在双视图转场时,我们可以根据是否有父视图,来判断谁进谁出
UIView *fromView =
nil
;
UIView *toView =
nil
;
if
(
self
.subView1.superview ==
nil
) {
// 说明subView1要转入
toView =
self
.subView1;
fromView =
self
.subView2;
}
else
{
// 说明subView2要转入
toView =
self
.subView2;
fromView =
self
.subView1;
}
[UIView transitionFromView:fromView toView:toView duration:1.0f options:UIViewAnimationOptionTransitionFlipFromTop completion:^(
BOOL
finished) {
NSLog
(
@"转场完成"
);
// 每次转场后,会调整参与转场视图的父视图,因此,参与转场视图的属性,需要是强引用
// 转场之后,入场的视图会有两个强引用,一个是视图控制器,另一个是视图
NSLog
(
@"subView1's superView: %@"
,
self
.subView1.superview);
NSLog
(
@"subView2's superView: %@"
,
self
.subView2.superview);
}];
|
8.6.1.使用双视图转场动画时,需要掌握视图的 superView 属性的变化。
8.6.2.方法调用完毕后,相当于执行了下面两句代码:
1
2
3
4
|
// 添加toView到父视图
[fromView.superview addSubview:toView];
// 把fromView从父视图中移除
[fromView.superview removeFromSuperview];
|
8.7.转场动画存在的问题是动画过程中无法交互,如果要在切换时实现交互效果需要使用 UIScrollView + UIPageControl 。
9.UIActivityIndicatorView
9.1.UIActivityIndicatorView 是一个旋转进度轮,可以用来告知用户有一个操作正在进行中,一般用initWithActivityIndicatorStyle 初始化。
9.2.相关方法:
- (void)startAnimating; | 开始动画 |
- (void)stopAnimating; | 停止动画 |
- (BOOL)isAnimating; | 是否正在运行动画 |
9.3.属性数值:
UIActivityIndicatorViewStyleWhiteLarge | 大型白色指示器 |
UIActivityIndicatorViewStyleWhite | 标准尺寸白色指示器 |
UIActivityIndicatorViewStyleGray | 灰色指示器,用于白色背景 |
9.3.常用第三方框架 SVProgressHUB
9.3.1.常用显示方法:showWithStatus ; showWithStatus:maskType:
9.3.2.关闭方法:dismiss
9.3.3.SVProgressHUB 对 UIActivityIndicatorView 进行了封装,增加了文字、图像以及容器视图等属性,通过类方法调用,使用简单。
9.3.4.使用 SVProgressHUDMaskTypeGradient 等属性显示提示信息时,可以使用 NSTimer 模拟等待后台响应效果。
10.UIImageView 的序列帧动画
10.1.UIImageView可以让一系列的图片在特定的时间内按顺序显示。
10.2.属性说明:
animationImages | 要显示的一组图片序列 |
animationDuration | 完整地显示所有图片所需的时间 |
animationRepeatCount | 动画的执行次数(默认为0,代表无限循环) |
10.3.相关方法:
- (void)startAnimating; | 开始动画 |
- (void)stopAnimating; | 停止动画 |
- (BOOL)isAnimating; | 是否正在运行动画 |
11.CADisplayLink
11.1.CADisplayLink 是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右。
11.2.CADisplayLink 是一个计时器,可以使绘图代码与视图的刷新频率保持同步,而 NSTimer 无法确保计时器实际被触发的准确时间。
11.3.使用方法:
11.3.1.定义 CADisplayLink 并制定触发调用方法
11.3.2.将显示链接添加到主运行循环队列
1
2
3
4
5
|
// 实例化游戏时钟
// 1. 实例化游戏时钟,并添加监听方法
self
.gameTimer = [CADisplayLink displayLinkWithTarget:
self
selector:
@selector
(step)];
// 2. 添加到主运行循环,否则监听方法不会被触发
[
self
.gameTimer addToRunLoop:[
NSRunLoop
mainRunLoop] forMode:
NSDefaultRunLoopMode
];
|