您的位置:首页 > 移动开发 > IOS开发

iOS——Core Animation(核心动画)

2016-08-27 00:00 295 查看

1、核心动画的基本概念

Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。

Core Animation是跨平台的,可以用在Mac OS X和iOS平台。

Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。不阻塞主线程,可以理解为在执行动画的时候还能点击(按钮)。

CAAnimation 简介:

CAAimation
是所有动画对象的基类,负责控制动画的持续时间和速度,是个抽象类,不能直接使⽤用,应该使⽤用它具体的⼦子类
属性说明:

removedOnCompletion
// 动画是否⾃自动移出

duration
// 动画持续时间

speed
//速度

timeOffset
// 动画时间的偏移

repeatCount
// 动画重复执⾏行的次数(HUGE_VALF无限次)

repeatDuration
// 动画重复执⾏行的总时间

autoreverses
// 反转动画

delegate
// 代理

fillMode
// 填充模式

timingFunction
//速度控制函数

基本属性说明:

属性说明
duration
动画的持续时间
repeatCount
重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT
repeatDuration
重复时间
removedOnCompletion
默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置
fillMode
kCAFillModeForwards

fillMode
决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之
beginTime
可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
timingFunction
速度控制函数,控制动画运行的节奏
delegate
动画代理
fillMode
属性的设置:


kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态

kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。

kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态

速度控制函数(CAMediaTimingFunction):

kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉

kCAMediaTimingFunctionEaseIn(加速):动画缓慢进入,然后加速离开

kCAMediaTimingFunctionEaseOut(减速):动画全速进入,然后减速的到达目的地

kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。

2、基本动画(CABasicAnimation)

CABasicAnimation 用于实现layer属性值从一个值(fromValue)到另外一个值(toValue)变化的简单动画,比如旋转、缩放、逐渐透明、移动等。

相关属性:

fromValue:keyPath相应属性的初始值

byValue:keyPath相应属性的中间值( 变化的值)

toValue:keyPath相应属性的结束值

动画过程说明:

随着动画的进⾏行,在⻓长度为duration的持续时间内,keyPath相应属性的值从

fromValue渐渐地变为toValue。

keyPath内容是CALayer的可动画Animatable属性

如果fillMode=kCAFillModeForwards同时removedOnComletion=NO,那么

在动画执行完毕后,图层会保持显示动画执行后的状态。但实质上,图层属性值还是动画执行前的初始值,并没有真正被改变。

代码事例如下:

#import "ViewController.h"

@interface ViewController (){

CALayer *aniLayer;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

aniLayer = [CALayer layer];
aniLayer.frame = CGRectMake(100, 50, 100, 100);
aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage);

[self.view.layer addSublayer:aniLayer];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

// 给layer 添加动画
[aniLayer addAnimation:[self positionAnimation] forKey:@"position"];
[aniLayer addAnimation:[self rotationAnimation] forKey:@"rotation"];

//    [aniLayer addAnimation:[self boundsAnimation] forKey:@"bounds"];

//    [aniLayer addAnimation:[self scaleAnimation] forKey:@"scale"];

}

- (CAAnimation *)positionAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 50)];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 550)];
animation.duration = 2.0;

animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.autoreverses = YES;
animation.repeatCount = HUGE_VALF; //  HUGE_VALF 最大浮点数,表示无限次重复

/* 动画的线性变换(动画速度变化)
kCAMediaTimingFunctionLinear 匀速
kCAMediaTimingFunctionEaseIn 加速
kCAMediaTimingFunctionEaseOut 减速
kCAMediaTimingFunctionEaseInEaseOut 缓慢进入缓慢出去
kCAMediaTimingFunctionDefault 默认
*/
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

return animation;
}

- (CAAnimation *)boundsAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
animation.fromValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)];
animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)];

animation.duration = 2.0;

return animation;
}

- (CAAnimation *)rotationAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// animation.fromValue = @0;
// animation.toValue = @(2 * M_PI);
animation.byValue = @( -2 * M_PI);

animation.duration = 2.0;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.autoreverses = YES;
animation.repeatCount = HUGE_VALF; //  HUGE_VALF 最大浮点数,表示无限次重复

return animation;
}

- (CAAnimation *)scaleAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];

// 1、初始值
animation.fromValue = @1.0;
// 2、目标值
animation.toValue = @2.0;

// 3、变化的值, fromValue ~ toValue 值的变化量
// animation.byValue = @1.0;

// 4、动画时间
animation.duration = 2.0;

/* 5、动画的填充模式:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved
*/
animation.fillMode = kCAFillModeForwards;

// 6、动画后是否移除动画后的状态(回到原始状态),默认是YES, 前提是要设置fillModle为:kCAFillModeForwards
animation.removedOnCompletion = NO;

// 7、是否有回复效果
animation.autoreverses = YES;

// 8、设置动画重复次数
animation.repeatCount = HUGE_VALF; //  HUGE_VALF 最大浮点数,表示无限次重复

// 9、播放动画的速度
animation.speed = 2;

return animation;
}

@end

暂停动画:

-(void)stopAnimation{
// CACurrentMediaTime(): 当前媒体时间,表示系统启动后到当前的秒数,当系统重启后这个时间也重置
CFTimeInterval stopTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
// 设置动画的时间的偏移
layer.timeOffset = stopTime;

layer.speed = 0;
}

继续动画:

-(void)resumeAnimation{
// 获取暂停时的时间
CFTimeInterval stopTime = [layer timeOffset];

layer.speed = 1;
layer.timeOffset = 0;
layer.beginTime = 0;

// 设置开始的时间(继续动画,这样设置相当于让动画等待的秒数等于暂停的秒)
layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - stopTime;
}

代码说明:

设置的
animationWithKeyPath
是@"position",说明要修改的是CALayer的position属性,也就是会执行平移动画

animation.fromValue
,
animation.toValue
这里的属性接收的时id类型的参数,因此并不能直接使用CGPoint这种结构体类型,而是要先包装成NSValue对象后再使用。

默认情况下,动画执行完毕后,动画会自动从CALayer上移除,CALayer又会回到原来的状态。为了保持动画执行后的状态,可以加入
animation.removedOnCompletion = NO


3、关键帧动画(CAKeyframeAnimation)

CAKeyframeAnimation 可以给一个图层提供多个目标值(values)或者一个指定路径(path)的动画。关键帧动画有如下几个重要参数:

values
指定图层属性(position、scale、rotation...)的多个目标值,这个图层就会由这些指定的值进行动画。

path
一个
CGPathRef
类型的路径,指定图层就会沿着这个路径进行动画。

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

代码事例如下:

#import "ViewController.h"

#define TScreenWidth [UIScreen mainScreen].bounds.size.width
#define TScreenHeight [UIScreen mainScreen].bounds.size.height

@interface ViewController (){

CALayer *aniLayer;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

CALayer *bgLayer = [CALayer layer];
bgLayer.frame = self.view.bounds;
bgLayer.backgroundColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:bgLayer];
bgLayer.delegate = self;

// 重绘
[bgLayer setNeedsDisplay];

aniLayer = [CALayer layer];
aniLayer.bounds = CGRectMake(0 , 0, 100, 100);
aniLayer.position = CGPointMake(TScreenWidth / 2, 50);
aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage);
[self.view.layer addSublayer:aniLayer];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

[aniLayer addAnimation:[self keyframeAnimation] forKey:@"keyAnimation"];
}

- (CAAnimation *)keyframeAnimation {

CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyAnimation.duration = 4;
//    keyAnimation.autoreverses = YES;
keyAnimation.repeatCount = HUGE_VALF;
keyAnimation.fillMode = kCAFillModeForwards;
keyAnimation.removedOnCompletion = NO;

/*
NSValue *point_1 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)];
NSValue *point_2 = [NSValue valueWithCGPoint:CGPointMake(50,TScreenHeight / 2)];
NSValue *point_3 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,TScreenHeight - 50)];
NSValue *point_4 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth - 50,TScreenHeight / 2)];
NSValue *point_5 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)];

// values:设置关键帧(多个目标点)
keyAnimation.values = @[point_1,point_2,point_3,point_4,point_5];

// 设置每一帧所在的时间比例
keyAnimation.keyTimes = @[@0, @0.2, @0.5, @0.6,@1.0];

*/
/* 插值计算模式:
kCAAnimationLinear  关键帧之间进行插值计算(线性的)
kCAAnimationDiscrete 关键帧之间不进行插值计算(离散的)
kCAAnimationPaced 关键帧之间匀速切换,keyTimes\timingFunctions的设置将不起作用
kCAAnimationCubic 关键帧进行圆滑曲线相连后插值计算
kCAAnimationCubicPaced 匀速并且关键帧进行圆滑曲线相连后插值计算
*/
keyAnimation.calculationMode = kCAAnimationLinear;

keyAnimation.path = [self path].CGPath;

return keyAnimation;

}

// 绘制路径
- (UIBezierPath *)path {

// 椭圆
//    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.view.bounds];
// 圆角矩形
//    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:50];
// 内切圆
//    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 100, 300, 300)];

// 贝塞尔曲线
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, TScreenHeight)];
CGPoint point_1 = CGPointMake(TScreenWidth, TScreenHeight);
CGPoint controPoint_1 = CGPointMake(TScreenWidth / 2, - TScreenHeight);
//    CGPoint controPoint_2 = CGPointMake(TScreenWidth / 4 * 3, TScreenHeight);

[path addQuadCurveToPoint:point_1 controlPoint:controPoint_1];
//    [path addCurveToPoint:point_1 controlPoint1:controPoint_1 controlPoint2:controPoint_2];

return path;
}

// 绘图
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

CGContextAddPath(ctx , [self path].CGPath);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextSetLineWidth(ctx, 5);
CGContextDrawPath(ctx, kCGPathStroke);
}

@end


CAKeyframeAnimation——计算模式属性

在关键帧动画中还有一个非常重要的参数,那便是calculationMode,所谓计算模式:其主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint和 position进行的动画

当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算

calculationMode目前提供如下几种模式:

kCAAnimationLinear: 默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算

kCAAnimationDiscrete: 离散的,不进行插值计算,所有关键帧直接逐个进行显示

kCAAnimationPaced :使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效

kCAAnimationCubic: 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,这里的主要目的是使得运行的轨迹变得圆滑

kCAAnimationCubicPaced :看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的

4、转场动画(CATransition)

CATransition是CAAnimation的子类,用于做转场动画

能够为图层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点

如:UINavigationController导航控制器就是通过CATransition转场动画实现了将控制器的视图推入屏 幕的动画效果

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

1.创建转场动画 CATransition

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

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

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

公开的API

fade 淡出效果 kCATransitionFade
movein 新视图移动到旧视图上 kCATransitionMoveIn
push 新视图退出旧视图上 kCATransitionPush
reveal 移开旧视图显示新视图 kCATransitionReveal

私有的API

cube 立体翻转效果
oglFlip 翻转效果
suckEffect 收缩效果
rippleEffect 水滴波纹效果
pageCurl 向上翻页效果
pageUnCurl 向下翻页效果
cameralIrisHollowOpen 摄像头打开效果
cameraIrisHollowClose 摄像头关闭效果

代码事例如下:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

static int index = 1;

[self.imageView.layer addAnimation:[self transitionAnimation] forKey:nil];

NSString *imageName = [NSString stringWithFormat:@"large_%d.jpg",index];
self.imageView.image = [UIImage imageNamed:imageName];

index++;

if (index > 10) {

index = 0;
}

}

- (CAAnimation *)transitionAnimation {

CATransition *transitionAni = [CATransition animation];
transitionAni.duration = 1.0;
/*
1. fade     淡出效果
2. moveIn 进入效果
3. push    推出效果
4. reveal   移出效果

// 未公开的
5. cube   立方体翻转效果
6. suckEffect  抽走效果
7. rippleEffect 水波效果
8. pageCurl    翻开页效果
9. pageUnCurl 关闭页效果
10. cameraIrisHollowOpen  相机镜头打开效果
11.  cameraIrisHollowClose  相机镜头关闭效果
*/

transitionAni.type = kCATransitionPush;
// transitionAni.type = @"push";

// 转场的方向:`fromLeft', `fromRight', `fromTop'  `fromBottom'
transitionAni.subtype = @"fromTop";

// 开始转场和结束转场的进度位置
// transitionAni.startProgress = 0.5;
// transitionAni.endProgress = 1;

return transitionAni;
}

@end


5、动画组(CAAnimationGrup)

CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。

UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。

相关属性: 1.type:动画过渡类型

2.subtype:动画过渡⽅方向

3.startProgress:动画起点

4.endProgress:动画终点

type属性说明:

1.kCATransitionFade:淡⼊入淡出

2.kCATransitionMoveIn:新视图移到旧视图上⾯面

3.kCATransitionPush:新视图把旧视图推出

4.kCATransitionReveal:把旧视图移开,显⽰示下⾯面的新视图

代码事例如下:

#import "ViewController.h"

#define TScreenWidth [UIScreen mainScreen].bounds.size.width

#define TScreenHeight [UIScreen mainScreen].bounds.size.height

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *bgView;
@property (weak, nonatomic) IBOutlet UIImageView *aniImageView;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

}

- (void)viewDidAppear:(BOOL)animated {

[super viewDidAppear:animated];

self.bgView.layer.delegate = self;
[self.bgView.layer setNeedsDisplay];

NSLog(@"%@",NSStringFromCGRect(self.aniImageView.frame));
NSLog(@"%f, %f",TScreenWidth, TScreenHeight);

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

[self.aniImageView.layer addAnimation:[self animationGroup] forKey:@"group"];
}

- (CAAnimation *)positionAnimation {

CAKeyframeAnimation *keyAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];

keyAni.duration = 3.0;
keyAni.fillMode = kCAFillModeForwards;
keyAni.removedOnCompletion = NO;
keyAni.path = [self path].CGPath;

return keyAni;
}

- (CAAnimation *)rotationAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];

animation.beginTime = 3.0;
animation.duration = 2.0;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.byValue = @(2 * M_PI);

return animation;
}

- (CAAnimation *)downAnimation {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];

animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.beginTime = 3.0;
animation.duration = 2.0;
animation.byValue = [NSValue valueWithCGPoint:CGPointMake(0, TScreenHeight)];

return animation;
}

- (UIBezierPath *)path  {

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, TScreenHeight)];
CGPoint toPoint = CGPointMake(TScreenWidth, 0);

CGPoint controlPoint = CGPointMake(TScreenWidth,TScreenHeight);
[path addQuadCurveToPoint:toPoint controlPoint:controlPoint];

return path;
}

- (CAAnimation *)animationGroup {

// 创建一个动画组,用于组合多种动画
CAAnimationGroup * aniGroup = [CAAnimationGroup animation];

// 动画组的完成时间
aniGroup.duration = 5.0;
aniGroup.fillMode = kCAFillModeForwards;
aniGroup.removedOnCompletion = NO;
// 组合多个动画
aniGroup.animations = @[[self positionAnimation],[self rotationAnimation],[self downAnimation]];
return aniGroup;
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

CGContextAddPath(ctx, [self path].CGPath);
CGContextSetLineWidth(ctx, 5);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextDrawPath(ctx, kCGPathStroke);
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS——Core Animation