关键帧动画

关键帧动画

熟悉flash开发的朋友对于关键帧动画应该不陌生,这种动画方式在flash开发中经常用到。关键帧动画就是在动画控制过程中开发者指定主要的动画状态,至于各个状态间动画如何进行则由系统自动运算补充(每两个关键帧之间系统形成的动画称为“补间动画”),这种动画的好处就是开发者不用逐个控制每个动画帧,而只要关心几个关键帧的状态即可。

关键帧动画开发分为两种形式:一种是通过设置不同的属性值进行关键帧控制,另一种是通过绘制路径进行关键帧控制。后者优先级高于前者,如果设置了路径则属性值就不再起作用。

对于前面的落花动画效果而言其实落花的过程并不自然,很显然实际生活中它不可能沿着直线下落,这里我们不妨通过关键帧动画的values属性控制它在下落过程中的属性。假设下落过程如图:

在这里需要设置四个关键帧(如图中四个关键点),具体代码如下(动画创建过程同基本动画基本完全一致):

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
50
51
52
53
54
55
56
57
58
//
//  通过values设置关键帧动画
//  Animation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
     CALayer *_layer;
}
 
@end
 
@implementation KCMainViewController
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景(注意这个图片其实在根图层)
     UIImage *backgroundImage=[UIImage imageNamed:@ "background.jpg" ];
     self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
     
     //自定义一个图层
     _layer=[[CALayer alloc]init];
     _layer.bounds=CGRectMake(0, 0, 10, 20);
     _layer.position=CGPointMake(50, 150);
     _layer.contents=(id)[UIImage imageNamed:@ "petal.png" ].CGImage;
     [self.view.layer addSublayer:_layer];
     
     //创建动画
     [self translationAnimation];
}
 
#pragma mark 关键帧动画
-(void)translationAnimation{
    
     //1.创建关键帧动画并设置动画属性
     CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@ "position" ];
     
     //2.设置关键帧,这里有四个关键帧
     NSValue *key1=[NSValue valueWithCGPoint:_layer.position]; //对于关键帧动画初始值不能省略
     NSValue *key2=[NSValue valueWithCGPoint:CGPointMake(80, 220)];
     NSValue *key3=[NSValue valueWithCGPoint:CGPointMake(45, 300)];
     NSValue *key4=[NSValue valueWithCGPoint:CGPointMake(55, 400)];
     NSArray *values=@[key1,key2,key3,key4];
     keyframeAnimation.values=values;
     //设置其他属性
     keyframeAnimation.duration=8.0;
     keyframeAnimation.beginTime=CACurrentMediaTime()+2; //设置延迟2秒执行
     
     
     //3.添加动画到图层,添加动画后就会执行动画
     [_layer addAnimation:keyframeAnimation forKey:@ "KCKeyframeAnimation_Position" ];
}
 
@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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//
//  通过path设置关键帧动画
//  Animation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
     CALayer *_layer;
}
 
@end
 
@implementation KCMainViewController
 
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景(注意这个图片其实在根图层)
     UIImage *backgroundImage=[UIImage imageNamed:@ "background.jpg" ];
     self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
     
     //自定义一个图层
     _layer=[[CALayer alloc]init];
     _layer.bounds=CGRectMake(0, 0, 10, 20);
     _layer.position=CGPointMake(50, 150);
     _layer.contents=(id)[UIImage imageNamed:@ "petal.png" ].CGImage;
     [self.view.layer addSublayer:_layer];
     
     //创建动画
     [self translationAnimation];
}
 
#pragma mark 关键帧动画
-(void)translationAnimation{
     //1.创建关键帧动画并设置动画属性
     CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@ "position" ];
     
     //2.设置路径
     //绘制贝塞尔曲线
     CGPathRef path=CGPathCreateMutable();
     CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y); //移动到起始点
     CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400); //绘制二次贝塞尔曲线
     keyframeAnimation.path=path; //设置path属性
     CGPathRelease(path); //释放路径对象
     //设置其他属性
     keyframeAnimation.duration=8.0;
     keyframeAnimation.beginTime=CACurrentMediaTime()+5; //设置延迟2秒执行
     
     
     //3.添加动画到图层,添加动画后就会执行动画
     [_layer addAnimation:keyframeAnimation forKey:@ "KCKeyframeAnimation_Position" ];
}
@end

运行效果(注意运行结束没有设置图层位置为动画运动结束位置):

看起来动画不会那么生硬了,但是这里需要注意,对于路径类型的关键帧动画系统是从描绘路径的位置开始路径,直到路径结束。如果上面的路径不是贝塞尔曲线而是矩形路径那么它会从矩形的左上角开始运行,顺时针一周回到左上角;如果指定的路径是一个椭圆,那么动画运行的路径是从椭圆右侧开始(0度)顺时针一周回到右侧。

补充--其他属性

在关键帧动画中还有一些动画属性初学者往往比较容易混淆,这里专门针对这些属性做一下介绍。

keyTimes:各个关键帧的时间控制。前面使用values设置了四个关键帧,默认情况下每两帧之间的间隔为:8/(4-1)秒。如果想要控制动画从第一帧到第二针占用时间4秒,从第二帧到第三帧时间为2秒,而从第三帧到第四帧时间2秒的话,就可以通过keyTimes进行设置。keyTimes中存储的是时间占用比例点,此时可以设置keyTimes的值为0.0,0.5,0.75,1.0(当然必须转换为NSNumber),也就是说1到2帧运行到总时间的50%,2到3帧运行到总时间的75%,3到4帧运行到8秒结束。

caculationMode:动画计算模式。还拿上面keyValues动画举例,之所以1到2帧能形成连贯性动画而不是直接从第1帧经过8/3秒到第2帧是因为动画模式是连续的(值为kCAAnimationLinear,这是计算模式的默认值);而如果指定了动画模式为kCAAnimationDiscrete离散的那么你会看到动画从第1帧经过8/3秒直接到第2帧,中间没有任何过渡。其他动画模式还有:kCAAnimationPaced(均匀执行,会忽略keyTimes)、kCAAnimationCubic(平滑执行,对于位置变动关键帧动画运行轨迹更平滑)、kCAAnimationCubicPaced(平滑均匀执行)。

下图描绘出了几种动画模式的关系(横坐标是运行时间,纵坐标是动画属性[例如位置、透明度等]):

动画组

实际开发中一个物体的运动往往是复合运动,单一属性的运动情况比较少,但恰恰属性动画每次进行动画设置时一次只能设置一个属性进行动画控制(不管是基础动画还是关键帧动画都是如此),这样一来要做一个复合运动的动画就必须创建多个属性动画进行组合。对于一两种动画的组合或许处理起来还比较容易,但是对于更多动画的组合控制往往会变得很麻烦,动画组的产生就是基于这样一种情况而产生的。动画组是一系列动画的组合,凡是添加到动画组中的动画都受控于动画组,这样一来各类动画公共的行为就可以统一进行控制而不必单独设置,而且放到动画组中的各个动画可以并发执行,共同构建出复杂的动画效果。

动画组使用起来并不复杂,首先单独创建单个动画(可以是基础动画也可以是关键帧动画),然后将基础动画添加到动画组,最后将动画组添加到图层即可。

前面关键帧动画部分,路径动画看起来效果虽然很流畅,但是落花本身的旋转运动没有了,这里不妨将基础动画部分的旋转动画和路径关键帧动画进行组合使得整个动画看起来更加的和谐、顺畅。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//
//  动画组
//  Animation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
 
     CALayer *_layer;
}
 
@end
 
@implementation KCMainViewController
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景(注意这个图片其实在根图层)
     UIImage *backgroundImage=[UIImage imageNamed:@ "background.jpg" ];
     self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
     
     //自定义一个图层
     _layer=[[CALayer alloc]init];
     _layer.bounds=CGRectMake(0, 0, 10, 20);
     _layer.position=CGPointMake(50, 150);
     _layer.contents=(id)[UIImage imageNamed:@ "petal.png" ].CGImage;
     [self.view.layer addSublayer:_layer];
     
     //创建动画
     [self groupAnimation];
}
 
#pragma mark 基础旋转动画
-(CABasicAnimation *)rotationAnimation{
 
     CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@ "transform.rotation.z" ];
     
     CGFloat toValue=M_PI_2*3;
     
     basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
//    basicAnimation.duration=6.0;
     basicAnimation.autoreverses= true ;
     basicAnimation.repeatCount=HUGE_VALF;
     basicAnimation.removedOnCompletion=NO;
     
     [basicAnimation setValue:[NSNumber numberWithFloat:toValue] forKey:@ "KCBasicAnimationProperty_ToValue" ];
     
     return  basicAnimation;
}
 
#pragma mark 关键帧移动动画
-(CAKeyframeAnimation *)translationAnimation{
     CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@ "position" ];
     
     CGPoint endPoint= CGPointMake(55, 400);
     CGPathRef path=CGPathCreateMutable();
     CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);
     CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, endPoint.x, endPoint.y);
     
     keyframeAnimation.path=path;
     CGPathRelease(path);
     
     [keyframeAnimation setValue:[NSValue valueWithCGPoint:endPoint] forKey:@ "KCKeyframeAnimationProperty_EndPosition" ];
     
     return  keyframeAnimation;
}
 
#pragma mark 创建动画组
-(void)groupAnimation{
     
     //1.创建动画组
     CAAnimationGroup *animationGroup=[CAAnimationGroup animation];
     
     //2.设置组中的动画和其他属性
     CABasicAnimation *basicAnimation=[self rotationAnimation];
     CAKeyframeAnimation *keyframeAnimation=[self translationAnimation];
     animationGroup.animations=@[basicAnimation,keyframeAnimation];
     
     animationGroup.delegate=self;
     animationGroup.duration=10.0; //设置动画时间,如果动画组中动画已经设置过动画属性则不再生效
     animationGroup.beginTime=CACurrentMediaTime()+5; //延迟五秒执行
     
     //3.给图层添加动画
     [_layer addAnimation:animationGroup forKey:nil];
}
 
#pragma mark - 代理方法
#pragma mark 动画完成
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
     CAAnimationGroup *animationGroup=(CAAnimationGroup *)anim;
     CABasicAnimation *basicAnimation=animationGroup.animations[0];
     CAKeyframeAnimation *keyframeAnimation=animationGroup.animations[1];
     CGFloat toValue=[[basicAnimation valueForKey:@ "KCBasicAnimationProperty_ToValue" ] floatValue];
     CGPoint endPoint=[[keyframeAnimation valueForKey:@ "KCKeyframeAnimationProperty_EndPosition" ] CGPointValue];
     
     [CATransaction begin];
     [CATransaction setDisableActions:YES];
     
     //设置动画最终状态
     _layer.position=endPoint;
     _layer.transform=CATransform3DMakeRotation(toValue, 0, 0, 1);
     
     [CATransaction commit];
}
 
@end

运行效果

 

转场动画

转场动画就是从一个场景以动画的形式过渡到另一个场景。转场动画的使用一般分为以下几个步骤:

1.创建转场动画

2.设置转场类型、子类型(可选)及其他属性

3.设置转场后的新视图并添加动画到图层

下表列出了常用的转场类型(注意私有API是苹果官方没有公开的动画类型,但是目前通过仍然可以使用):

另外对于支持方向设置的动画类型还包含子类型:

在前面的文章“iOS开发系列--无限循环的图片浏览器”中为了使用UIScrollView做无限循环图片浏览器花费了不少时间在性能优化上面,这里使用转场动画利用一个UIImageView实现一个漂亮的无限循环图片浏览器。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//
//  KCMainViewController.m
//  TransitionAnimation
//
//  Created by Kenshin Cui on 14-3-12.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
#define IMAGE_COUNT 5
@interface KCMainViewController (){
     UIImageView *_imageView;
     int _currentIndex;
}
 
@end
 
@implementation KCMainViewController
 
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //定义图片控件
     _imageView=[[UIImageView alloc]init];
     _imageView.frame=[UIScreen mainScreen].applicationFrame;
     _imageView.contentMode=UIViewContentModeScaleAspectFit;
     _imageView.image=[UIImage imageNamed:@ "0.jpg" ]; //默认图片
     [self.view addSubview:_imageView];
     //添加手势
     UISwipeGestureRecognizer *leftSwipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
     leftSwipeGesture.direction=UISwipeGestureRecognizerDirectionLeft;
     [self.view addGestureRecognizer:leftSwipeGesture];
     
     UISwipeGestureRecognizer *rightSwipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
     rightSwipeGesture.direction=UISwipeGestureRecognizerDirectionRight;
     [self.view addGestureRecognizer:rightSwipeGesture];
}
 
#pragma mark 向左滑动浏览下一张图片
-(void)leftSwipe:(UISwipeGestureRecognizer *)gesture{
     [self transitionAnimation:YES];
}
 
#pragma mark 向右滑动浏览上一张图片
-(void)rightSwipe:(UISwipeGestureRecognizer *)gesture{
     [self transitionAnimation:NO];
}
 
#pragma mark 转场动画
-(void)transitionAnimation:(BOOL)isNext{
     //1.创建转场动画对象
     CATransition *transition=[[CATransition alloc]init];
     
     //2.设置动画类型,注意对于苹果官方没公开的动画类型只能使用字符串,并没有对应的常量定义
     transition.type=@ "cube" ;
     
     //设置子类型
     if  (isNext) {
         transition.subtype=kCATransitionFromRight;
     } else {
         transition.subtype=kCATransitionFromLeft;
     }
     //设置动画时常
     transition.duration=1.0f;
     
     //3.设置转场后的新视图添加转场动画
     _imageView.image=[self getImage:isNext];
     [_imageView.layer addAnimation:transition forKey:@ "KCTransitionAnimation" ];
}
 
#pragma mark 取得当前图片
-(UIImage *)getImage:(BOOL)isNext{
     if  (isNext) {
         _currentIndex=(_currentIndex+1)%IMAGE_COUNT;
     } else {
         _currentIndex=(_currentIndex-1+IMAGE_COUNT)%IMAGE_COUNT;
     }
     NSString *imageName=[NSString stringWithFormat:@ "%i.jpg" ,_currentIndex];
     return  [UIImage imageNamed:imageName];
}
@end


运行效果

代码十分简单,但是效果和性能却很惊人。当然演示代码有限,其他动画类型大家可以自己实现,效果都很绚丽。

逐帧动画

前面介绍了核心动画中大部分动画类型,但是做过动画处理的朋友都知道,在动画制作中还有一种动画类型“逐帧动画”。说到逐帧动画相信很多朋友第一个想到的就是UIImageView,通过设置UIImageView的animationImages属性,然后调用它的startAnimating方法去播放这组图片。当然这种方法在某些场景下是可以达到逐帧的动画效果,但是它也存在着很大的性能问题,并且这种方法一旦设置完图片中间的过程就无法控制了。当然,也许有朋友会想到利用iOS的定时器NSTimer定时更新图片来达到逐帧动画的效果。这种方式确实可以解决UIImageView一次性加载大量图片的问题,而且让播放过程可控,唯一的缺点就是定时器方法调用有时可能会因为当前系统执行某种比较占用时间的任务造成动画连续性出现问题。

虽然在核心动画没有直接提供逐帧动画类型,但是却提供了用于完成逐帧动画的相关对象CADisplayLink。CADisplayLink是一个计时器,但是同NSTimer不同的是,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒,这样一来使用它完成的逐帧动画(又称为“时钟动画”)完全感觉不到动画的停滞情况。

在iOS开篇“IOS开发系列--IOS程序开发概览”中就曾说过:iOS程序在运行后就进入一个消息循环中(这个消息循环称为“主运行循环”),整个程序相当于进入一个死循环中,始终等待用户输入。将CADisplayLink加入到主运行循环队列后,它的时钟周期就和主运行循环保持一致,而主运行循环周期就是屏幕刷新周期。在CADisplayLink加入到主运行循环队列后就会循环调用目标方法,在这个方法中更新视图内容就可以完成逐帧动画。

当然这里不得不强调的是逐帧动画性能势必较低,但是对于一些事物的运动又不得不选择使用逐帧动画,例如人的运动,这是一个高度复杂的运动,基本动画、关键帧动画是不可能解决的。所大家一定要注意在循环方法中尽可能的降低算法复杂度,同时保证循环过程中内存峰值尽可能低。下面以一个鱼的运动为例为大家演示一下逐帧动画。

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
50
51
52
53
54
55
56
57
58
59
//
//  KCMainViewController.m
//  DisplayLink
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
#define IMAGE_COUNT 10
 
@interface KCMainViewController (){
     CALayer *_layer;
     int _index;
     NSMutableArray *_images;
}
 
@end
 
@implementation KCMainViewController
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景
     self.view.layer.contents=(id)[UIImage imageNamed:@ "bg.png" ].CGImage;
     
     //创建图像显示图层
     _layer=[[CALayer alloc]init];
     _layer.bounds=CGRectMake(0, 0, 87, 32);
     _layer.position=CGPointMake(160, 284);
     [self.view.layer addSublayer:_layer];
     
     //由于鱼的图片在循环中会不断创建,而10张鱼的照片相对都很小
     //与其在循环中不断创建UIImage不如直接将10张图片缓存起来
     _images=[NSMutableArray array];
     for  (int i=0; i<10; ++i) {
         NSString *imageName=[NSString stringWithFormat:@ "fish%i.png" ,i];
         UIImage *image=[UIImage imageNamed:imageName];
         [_images addObject:image];
     }
     
     //定义时钟对象
     CADisplayLink *displayLink=[CADisplayLink displayLinkWithTarget:self selector:@selector(step)];
     //添加时钟对象到主运行循环
     [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
 
#pragma mark 每次屏幕刷新就会执行一次此方法(每秒接近60次)
-(void)step{
     //定义一个变量记录执行次数
     static int s=0;
     //每秒执行6次
     if  (++s==0) {
         UIImage *image=_images[_index];
         _layer.contents=(id)image.CGImage; //更新图片
         _index=(_index+1)%IMAGE_COUNT;
     }
}
@end

运行效果

注意:上面仅仅演示了逐帧动画的过程,事实上结合其他动画类型可以让整条鱼游动起来,这里不再赘述。

UIView动画封装

有了前面核心动画的知识,相信大家开发出一般的动画效果应该不在话下。在核心动画开篇也给大家说过,其实UIView本身对于基本动画和关键帧动画、转场动画都有相应的封装,在对动画细节没有特殊要求的情况下使用起来也要简单的多。可以说在日常开发中90%以上的情况使用UIView的动画封装方法都可以搞定,因此在熟悉了核心动画的原理之后还是有必要给大家简单介绍一下UIView中各类动画使用方法的。由于前面核心动画内容已经进行过详细介绍,学习UIView的封装方法根本是小菜一碟,这里对于一些细节就不再赘述了。

基础动画

基础动画部分和前面的基础动画演示相对应,演示点击屏幕落叶飘落到鼠标点击位置的过程。注意根据前面介绍的隐式动画知识其实非根图层直接设置终点位置不需要使用UIView的动画方法也可以实现动画效果,因此这里落花不再放到图层中而是放到了一个UIImageView中。

下面的代码演示了通过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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//
//  UIView实现基础动画
//  Animation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
     UIImageView *_imageView;
}
 
@end
 
@implementation KCMainViewController
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景
     UIImage *backgroundImage=[UIImage imageNamed:@ "background.jpg" ];
     self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
     
     //创建图像显示控件
     _imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@ "petal.png" ]];
     _imageView.center=CGPointMake(50, 150);
     [self.view addSubview:_imageView];
}
 
#pragma mark 点击事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
     UITouch *touch=touches.anyObject;
     CGPoint location= [touch locationInView:self.view];
     //方法1:block方式
     /*开始动画,UIView的动画方法执行完后动画会停留在重点位置,而不需要进行任何特殊处理
      duration:执行时间
      delay:延迟时间
      options:动画设置,例如自动恢复、匀速运动等
      completion:动画完成回调方法
      */
//    [UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
//        _imageView.center=location;
//    } completion:^(BOOL finished) {
//        NSLog(@"Animation end.");
//    }];
     
     //方法2:静态方法
     //开始动画
     [UIView beginAnimations:@ "KCBasicAnimation"  context:nil];
     [UIView setAnimationDuration:5.0];
     //[UIView setAnimationDelay:1.0];//设置延迟
     //[UIView setAnimationRepeatAutoreverses:NO];//是否回复
     //[UIView setAnimationRepeatCount:10];//重复次数
     //[UIView setAnimationStartDate:(NSDate *)];//设置动画开始运行的时间
     //[UIView setAnimationDelegate:self];//设置代理
     //[UIView setAnimationWillStartSelector:(SEL)];//设置动画开始运动的执行方法
     //[UIView setAnimationDidStopSelector:(SEL)];//设置动画运行结束后的执行方法
     
     _imageView.center=location;
     
     
     //开始动画
     [UIView commitAnimations];
}
@end

补充--弹簧动画效果

由于在iOS开发中弹性动画使用很普遍,所以在iOS7苹果官方直接提供了一个方法用于弹性动画开发,下面简单的演示一下:

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
//
//  UIView实现基础动画--弹性动画
//  Animation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
 
     UIImageView *_imageView;
}
 
@end
 
@implementation KCMainViewController
 
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //创建图像显示控件
     _imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@ "ball.png" ]];
     _imageView.center=CGPointMake(160, 50);
     [self.view addSubview:_imageView];
}
 
#pragma mark 点击事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
     UITouch *touch=touches.anyObject;
     CGPoint location= [touch locationInView:self.view];
     /*创建弹性动画
      damping:阻尼,范围0-1,阻尼越接近于0,弹性效果越明显
      velocity:弹性复位的速度
     */
     [UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
         _imageView.center=location;  //CGPointMake(160, 284);
     } completion:nil];
}
@end

运行效果

补充--动画设置参数

在动画方法中有一个option参数,UIViewAnimationOptions类型,它是一个枚举类型,动画参数分为三类,可以组合使用:

1.常规动画属性设置(可以同时选择多个进行设置)

  • UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。

  • UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。

  • UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。

  • UIViewAnimationOptionRepeat:重复运行动画。

  • UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。

  • UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。

  • UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套动画速度设置。

  • UIViewAnimationOptionAllowAnimatedContent:动画过程中重绘视图(注意仅仅适用于转场动画)。 

  • UIViewAnimationOptionShowHideTransitionViews:视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)

  • UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。

2.动画速度控制(可从其中选择一个设置)

  • UIViewAnimationOptionCurveEaseInOut:动画先缓慢,然后逐渐加速。

  • UIViewAnimationOptionCurveEaseIn :动画逐渐变慢。

  • UIViewAnimationOptionCurveEaseOut:动画逐渐加速。

  • UIViewAnimationOptionCurveLinear :动画匀速执行,默认值。

3.转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)

  • UIViewAnimationOptionTransitionNone:没有转场动画效果。

  • UIViewAnimationOptionTransitionFlipFromLeft :从左侧翻转效果。

  • UIViewAnimationOptionTransitionFlipFromRight:从右侧翻转效果。

  • UIViewAnimationOptionTransitionCurlUp:向后翻页的动画过渡效果。   

  • UIViewAnimationOptionTransitionCurlDown :向前翻页的动画过渡效果。   

  • UIViewAnimationOptionTransitionCrossDissolve:旧视图溶解消失显示下一个新视图的效果。   

  • UIViewAnimationOptionTransitionFlipFromTop :从上方翻转效果。   

  • UIViewAnimationOptionTransitionFlipFromBottom:从底部翻转效果。

关键帧动画

从iOS7开始UIView动画中封装了关键帧动画,下面就来看一下如何使用UIView封装方法进行关键帧动画控制,这里实现前面关键帧动画部分对于落花的控制。

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
50
51
52
53
54
55
56
//
//  UIView关键帧动画
//  UIViewAnimation
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
 
@interface KCMainViewController (){
     UIImageView *_imageView;
}
 
@end
 
@implementation KCMainViewController
 
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //设置背景
     UIImage *backgroundImage=[UIImage imageNamed:@ "background.jpg" ];
     self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
     
     //创建图像显示控件
     _imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@ "petal.png" ]];
     _imageView.center=CGPointMake(50, 150);
     [self.view addSubview:_imageView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
     //UITouch *touch=touches.anyObject;
     //CGPoint location= [touch locationInView:self.view];
     
     /*关键帧动画
      options:
      */
     [UIView animateKeyframesWithDuration:5.0 delay:0 options: UIViewAnimationOptionCurveLinear| UIViewAnimationOptionCurveLinear animations:^{
         //第二个关键帧(准确的说第一个关键帧是开始位置):从0秒开始持续50%的时间,也就是5.0*0.5=2.5秒
         [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
             _imageView.center=CGPointMake(80.0, 220.0);
         }];
         //第三个关键帧,从0.5*5.0秒开始,持续5.0*0.25=1.25秒
         [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
             _imageView.center=CGPointMake(45.0, 300.0);
         }];
         //第四个关键帧:从0.75*5.0秒开始,持所需5.0*0.25=1.25秒
         [UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
             _imageView.center=CGPointMake(55.0, 400.0);
         }];
         
     } completion:^(BOOL finished) {
         NSLog(@ "Animation end." );
     }];
}
@end

补充--动画设置参数

对于关键帧动画也有一些动画参数设置options,UIViewKeyframeAnimationOptions类型,和上面基本动画参数设置有些差别,关键帧动画设置参数分为两类,可以组合使用:

1.常规动画属性设置(可以同时选择多个进行设置)

  • UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。

  • UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。

  • UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。

  • UIViewAnimationOptionRepeat:重复运行动画。

  • UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。

  • UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。

  • UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。

2.动画模式设置(同前面关键帧动画动画模式一一对应,可以从其中选择一个进行设置)

  • UIViewKeyframeAnimationOptionCalculationModeLinear:连续运算模式。

  • UIViewKeyframeAnimationOptionCalculationModeDiscrete :离散运算模式。

  • UIViewKeyframeAnimationOptionCalculationModePaced:均匀执行运算模式。

  • UIViewKeyframeAnimationOptionCalculationModeCubic:平滑运算模式。

  • UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均匀运算模式。

注意:前面说过关键帧动画有两种形式,上面演示的是属性值关键帧动画,路径关键帧动画目前UIView还不支持。

转场动画

从iOS4.0开始,UIView直接封装了转场动画,使用起来同样很简单。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//
//  UIView转场动画
//  TransitionAnimation
//
//  Created by Kenshin Cui on 14-3-12.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
 
#import "KCMainViewController.h"
#define IMAGE_COUNT 5
 
@interface KCMainViewController (){
     UIImageView *_imageView;
     int _currentIndex;
}
 
@end
 
@implementation KCMainViewController
 
- (void)viewDidLoad {
     [ super  viewDidLoad];
     
     //定义图片控件
     _imageView=[[UIImageView alloc]init];
     _imageView.frame=[UIScreen mainScreen].applicationFrame;
     _imageView.contentMode=UIViewContentModeScaleAspectFit;
     _imageView.image=[UIImage imageNamed:@ "0.jpg" ]; //默认图片
     [self.view addSubview:_imageView];
     //添加手势
     UISwipeGestureRecognizer *leftSwipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
     leftSwipeGesture.direction=UISwipeGestureRecognizerDirectionLeft;
     [self.view addGestureRecognizer:leftSwipeGesture];
     
     UISwipeGestureRecognizer *rightSwipeGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
     rightSwipeGesture.direction=UISwipeGestureRecognizerDirectionRight;
     [self.view addGestureRecognizer:rightSwipeGesture];
}
#pragma mark 向左滑动浏览下一张图片
-(void)leftSwipe:(UISwipeGestureRecognizer *)gesture{
     [self transitionAnimation:YES];
}
 
#pragma mark 向右滑动浏览上一张图片
-(void)rightSwipe:(UISwipeGestureRecognizer *)gesture{
     [self transitionAnimation:NO];
}
 
#pragma mark 转场动画
-(void)transitionAnimation:(BOOL)isNext{
     UIViewAnimationOptions option;
     if  (isNext) {
         option=UIViewAnimationOptionCurveLinear|UIViewAnimationOptionTransitionFlipFromRight;
     } else {
         option=UIViewAnimationOptionCurveLinear|UIViewAnimationOptionTransitionFlipFromLeft;
     }
     
     [UIView transitionWithView:_imageView duration:1.0 options:option animations:^{
         _imageView.image=[self getImage:isNext];
     } completion:nil];
}
 
#pragma mark 取得当前图片
-(UIImage *)getImage:(BOOL)isNext{
     if  (isNext) {
         _currentIndex=(_currentIndex+1)%IMAGE_COUNT;
     } else {
         _currentIndex=(_currentIndex-1+IMAGE_COUNT)%IMAGE_COUNT;
     }
     NSString *imageName=[NSString stringWithFormat:@ "%i.jpg" ,_currentIndex];
     return  [UIImage imageNamed:imageName];
}
@end

上面的转场动画演示中,其实仅仅有一个视图UIImageView做转场动画,每次转场通过切换UIImageView的内容而已。如果有两个完全不同的视图,并且每个视图布局都很复杂,此时要在这两个视图之间进行转场可以使用+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0)方法进行两个视图间的转场,需要注意的是默认情况下转出的视图会从父视图移除,转入后重新添加,可以通过UIViewAnimationOptionShowHideTransitionViews参数设置,设置此参数后转出的视图会隐藏(不会移除)转入后再显示。

注意:转场动画设置参数完全同基本动画参数设置;同直接使用转场动画不同的是使用UIView的装饰方法进行转场动画其动画效果较少,因为这里无法直接使用私有API。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值