iOS中实现动画有两种方式,一种是自己不断的通过drawRect:方法来绘制,另外一种就是使用核心动画(Core Animation)。

  导语:

  核心动画提供高帧速率和流畅的动画,而不会增加CPU的负担和减慢你的应用程序。换句话说,使用核心动画你就不用担心性能的事了。同时,使用核心动画只需要提供少数参数,使用起来很简单。需要注意的是核心动画针对的是CALayer而不是UIView,所以对使用核心动画前,需要先对CALayer的知识有过了解。

  一、系统层级介绍

  如图所示,核心动画位于AppKit和UIKit之下,并紧密集成到Cocoa和Cocoa Touch的视图工作流中。

  

  二、核心动画类图介绍

  先来一张类图(ps:这类图是盗的,等我会画的时候就自己画了)。如图所示,CAAnimation作为虚基类实现了CAMediaTiming协议(其实还实现了CAAction协议)。CAAnimation有三个子类CAAnimationGroup(组动画)、CAPropertyAnimation(属性动画)、CATrasition(渐变动画)。CAAnimation不能直接使用,应该使用它的子类。作为CAPropertyAnimation也有两个子类CABasicAnimation(基础动画)、CAKeyFrameAnimation(关键帧动画)。CAPropertyAnimation也不能直接使用,应该使用两个子类。综上所诉要使用核心动画,可以使用的就是以下四个类(CAAnimationGroup、CATrasition、CABasicAnimation、CAKeyFrameAnimation)。

  PS:在iOS9.0+以后,核心动画又加入了CASpringAnimation(弹性动画),CASpringAnimation继承自CABasicAnimation。

  

  三、基本使用示例

  在这里先介绍一下代码统一的代码,所有的示例代码均继承父类创建TestLayer代码

self.testLayer = ({
CALayer *tempLayer = [CALayer new];
tempLayer.backgroundColor = [UIColor cyanColor].CGColor;
tempLayer.position = self.view.center;
tempLayer.bounds = CGRectMake(, , , );
[self.view.layer addSublayer:tempLayer];
tempLayer;
});

  首先要介绍CAPropertyAnimation(属性动画),属性动画创建的时候需要指定keyPath,可以动画的属性可以在官网文档查看

  3.1 CABasicAnimation

  使用代码示例

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
positionAnimation.fromValue = [NSValue valueWithCGPoint:self.testLayer.presentationLayer.position];
positionAnimation.toValue = [NSValue valueWithCGPoint:point];
positionAnimation.duration = .f;//动画时长
positionAnimation.removedOnCompletion = NO;//是否在完成时移除
positionAnimation.fillMode = kCAFillModeForwards;//动画结束后是否保持状态
[self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"];
}

  上面的示例代码通过使用CABasicAnimation来实现了位置动画,让testLayer每次动画移动到点击的位置,在使用基础动画的时候需要指定三个属性:fromValue(可省略,默认值为动画的keyPath对应的当前属性值),toValue,duration(默认值为0.25s)。

  运行效果如下:

  

  3.2 隐式动画

  如果对于基础动画,不需要设置其他值,仅仅想要设置toValue来实现动画的话,那么就可以使用隐式动画。隐式动画其实通过直接修改layer的动画属性,系统会按照基础动画的默认值来实现动画。代码如下

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
self.testLayer.position = [touch locationInView:self.view];//修改位置的隐式动画
CGFloat WH = arc4random_uniform();
if (WH < ) {
WH += ;
}
self.testLayer.bounds = CGRectMake(, , WH, WH);
UIColor *color = [UIColor colorWithRed:arc4random_uniform() / 255.0 green:arc4random_uniform() / 255.0 blue:arc4random_uniform() / 255.0 alpha:.f];
self.testLayer.backgroundColor = color.CGColor;//修改背景色的隐式动画
}

  效果如下

  

  

  3.3 CAKeyFrameAnimation

  关键帧动画的使用中可以设置path,也可以设置values,在设置values的时候,默认会把动画时间按照values的个数进行平均分配。下面是使用path来做的动画,同时显示了轨迹。

  先上效果:

  

  代码如下:

- (void)viewDidLoad {
[super viewDidLoad]; NSLog(@"CAKeyframeAnimation");
//重新设置初始位置
self.testLayer.position = CGPointMake(33.5, 409.5);
self.testLayer.bounds = CGRectMake(, , , ); CGPathRef bezirePath = [self bezirePath]; //绘制轨迹
CAShapeLayer *positionTrackLayer = [[CAShapeLayer alloc] init]; positionTrackLayer.path = bezirePath;
positionTrackLayer.strokeColor = [UIColor redColor].CGColor;
positionTrackLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:positionTrackLayer]; //添加保存动画
self.positionAnimation = [self keyframeAnimation:bezirePath];
} - (CGPathRef)bezirePath
{
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
CGPoint fromPoint = CGPointMake(33.5, 409.5);
[bezierPath moveToPoint: fromPoint];
[bezierPath addCurveToPoint: CGPointMake(127.5, 119.78) controlPoint1: CGPointMake(58.5, 433.93) controlPoint2: CGPointMake(86.5, 95.16)];
[bezierPath addCurveToPoint: CGPointMake(183.5, 554.5) controlPoint1: CGPointMake(168.5, 144.4) controlPoint2: CGPointMake(112.5, 557.05)];
[bezierPath addCurveToPoint: CGPointMake(251.5, 119.78) controlPoint1: CGPointMake(254.5, 551.95) controlPoint2: CGPointMake(251.5, 119.78)];
return bezierPath.CGPath;
} - (CAKeyframeAnimation *)keyframeAnimation:(CGPathRef)bezirePath
{
CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; moveAnimation.path = bezirePath;
moveAnimation.fillMode = kCAFillModeForwards;
moveAnimation.removedOnCompletion = NO;
moveAnimation.duration = .f; return moveAnimation;
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.testLayer addAnimation:self.positionAnimation forKey:@"position"];
}

  3.4 AnimationGroup(组动画)

  组动画的作用是可以把多个动画组合在一起比如移动、旋转、缩放、透明度等等。在使用动画组时需要先将需要组合的动画创建好,最后放到CAAnimationGroup的animations数组中即可,animationGroup其他设置与基础动画差不多

  先来一个类似于漂浮气泡的动画

  

  这个动画的实现实际上就是使用组动画来同时改变layer在x,y轴上的缩放、x,y轴上的移动来实现的,以下为实现代码:

- (void)viewDidLoad {
[super viewDidLoad]; NSLog(@"CAAnimationGroup"); //重新设置layer大小与圆角
self.testLayer.bounds = CGRectMake(, , , );
self.testLayer.cornerRadius = / ; //设置x轴方向的缩放动画
CAKeyframeAnimation *xScaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
xScaleAnimation.values = @[@, @0.9, @, @1.1, @0.9, @];
xScaleAnimation.duration = .f;
xScaleAnimation.repeatCount = CGFLOAT_MAX;
xScaleAnimation.removedOnCompletion = NO;
xScaleAnimation.fillMode = kCAFillModeForwards; //设置y轴方向的缩放动画
CAKeyframeAnimation *yScaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
yScaleAnimation.values = @[@0.9, @, @1.1, @0.8, @, @0.9];
yScaleAnimation.duration = .f;
yScaleAnimation.repeatCount = CGFLOAT_MAX;
yScaleAnimation.removedOnCompletion = NO;
yScaleAnimation.fillMode = kCAFillModeForwards; //设置x轴方向的移动动画
CAKeyframeAnimation *xTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
xTranslationAnimation.values = @[@, @, @(-), @, @, @];
xTranslationAnimation.duration = .f;
xTranslationAnimation.repeatCount = CGFLOAT_MAX;
xTranslationAnimation.removedOnCompletion = NO;
xTranslationAnimation.fillMode = kCAFillModeForwards; //设置y轴方向的移动动画
CAKeyframeAnimation *yTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
yTranslationAnimation.values = @[@, @, @, @-, @];
yTranslationAnimation.duration = .f;
yTranslationAnimation.repeatCount = CGFLOAT_MAX;
yTranslationAnimation.removedOnCompletion = NO;
yTranslationAnimation.fillMode = kCAFillModeForwards; //组动画
CAAnimationGroup *groupAnimation = [[CAAnimationGroup alloc] init];
groupAnimation.animations = @[xScaleAnimation, yScaleAnimation, xTranslationAnimation, yTranslationAnimation];//将所有动画添加到动画组
groupAnimation.duration = .f;
groupAnimation.repeatCount = CGFLOAT_MAX;
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards; [self.testLayer addAnimation:groupAnimation forKey:@"groupAnimation"];
}

  3.5 CATransition(过渡动画)

  过渡动画的使用方式和属性动画就不同了,还是先来效果

  对于具体的解释见代码:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CATransition *transition = [CATransition animation];
transition.startProgress = ;//开始进度
transition.endProgress = ;//结束进度
transition.type = kCATransitionReveal;//过渡类型
transition.subtype = kCATransitionFromLeft;//过渡方向
transition.duration = .f; UIColor *color = [UIColor colorWithRed:arc4random_uniform() / 255.0 green:arc4random_uniform() / 255.0 blue:arc4random_uniform() / 255.0 alpha:.f];
self.testLayer.backgroundColor = color.CGColor; [self.testLayer addAnimation:transition forKey:@"transition"];
}

  以上就是核心动画的基本使用了

  接下来介绍核心动画的进阶使用

四、核心动画进阶使用

  核心动画允许我们在动画过程中进行操作,如:暂停、恢复、移除

  4.1 动画的暂停与恢复

  暂停动画需要做两步操作:

  1. 利用layer的timeOffset来记录当前暂停的时间点

  2. 设置layer的speed为0

  恢复动画需要做以下操作:

  1. 取出暂停时间的时间点

  2. 恢复speed为1

  3. 设置timeOffset为0

  4. 设置beginTimer为0

  5. 计算当前时间与暂停时间点的时间差

  6. 将beginTimer设置为计算出来的时间差

  还是先来一个暂停效果:

  

  具体的暂停与恢复的代码如下:

@interface ViewController6 ()

@property (nonatomic, strong) CAKeyframeAnimation *positionAnimation;
@property (nonatomic, assign) BOOL isPositionAnimation; @end @implementation ViewController6 - (void)viewDidLoad {
[super viewDidLoad]; NSLog(@"动画的暂停与恢复");
//重新设置初始位置
self.testLayer.position = CGPointMake(33.5, 409.5);
self.testLayer.bounds = CGRectMake(, , , ); CGPathRef bezirePath = [self bezirePath]; //绘制轨迹
CAShapeLayer *positionTrackLayer = [[CAShapeLayer alloc] init]; positionTrackLayer.path = bezirePath;
positionTrackLayer.strokeColor = [UIColor redColor].CGColor;
positionTrackLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:positionTrackLayer]; //添加保存动画
self.positionAnimation = [self keyframeAnimation:bezirePath];
} - (CGPathRef)bezirePath
{
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
CGPoint fromPoint = CGPointMake(33.5, 409.5);
[bezierPath moveToPoint: fromPoint];
[bezierPath addCurveToPoint: CGPointMake(127.5, 119.78) controlPoint1: CGPointMake(58.5, 433.93) controlPoint2: CGPointMake(86.5, 95.16)];
[bezierPath addCurveToPoint: CGPointMake(183.5, 554.5) controlPoint1: CGPointMake(168.5, 144.4) controlPoint2: CGPointMake(112.5, 557.05)];
[bezierPath addCurveToPoint: CGPointMake(251.5, 119.78) controlPoint1: CGPointMake(254.5, 551.95) controlPoint2: CGPointMake(251.5, 119.78)];
return bezierPath.CGPath;
} - (CAKeyframeAnimation *)keyframeAnimation:(CGPathRef)bezirePath
{
CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; moveAnimation.path = bezirePath;
moveAnimation.fillMode = kCAFillModeForwards;
moveAnimation.removedOnCompletion = NO;
moveAnimation.duration = .f; return moveAnimation;
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if ([self.testLayer animationForKey:[self.testLayer.animationKeys firstObject]] == nil) {
[self.testLayer addAnimation:self.positionAnimation forKey:@"position"]; self.isPositionAnimation = YES;
return;
} if (self.isPositionAnimation) {
[self pauseLayer:self.testLayer];
self.isPositionAnimation = NO;
}else{
self.isPositionAnimation = YES;
[self resumeLayer:self.testLayer];
}
} #pragma mark - 动画的暂停与恢复 /**
暂停动画 @param layer 要暂停的layer
*/
-(void)pauseLayer:(CALayer *)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
} /**
恢复动画 @param layer 要恢复的layer
*/
-(void)resumeLayer:(CALayer *)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}

  4.2 监听动画的完成状态

  有时候我们需要监听动画的开始与完成,这就要靠设置动画的delegate来实现了

 

@protocol CAAnimationDelegate <NSObject>
@optional //在动画开始时被调用
- (void)animationDidStart:(CAAnimation *)anim; //在动画结束时被调用, 如果是动画被移除则flag为false,正常结束为true
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; @end

  示例代码如下:

  需要注意的是

- (void)viewDidLoad {
[super viewDidLoad]; NSLog(@"监听动画完成状态");
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
CGPoint fromPoint = CGPointMake(, );
positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];
positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(, )];
positionAnimation.duration = .f;
positionAnimation.fillMode = kCAFillModeForwards;
positionAnimation.removedOnCompletion = NO; positionAnimation.delegate = self; [positionAnimation setValue:@"位置动画" forKey:@"animationName"];//添加属性名 [self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"];
} //在动画开始时被调用
- (void)animationDidStart:(CAAnimation *)anim
{
NSLog(@"%@-----动画开始", [anim valueForKey:@"animationName"]);
} //在动画结束时被调用, 如果是动画被移除则flag为false,正常结束为true
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if (flag) {
NSLog(@"%@-----动画正常结束", [anim valueForKey:@"animationName"]);
}else{
NSLog(@"%@-----动画移除结束", [anim valueForKey:@"animationName"]);
}
}

  4.2  CATransaction(事务)的使用

    4.2.1 关闭隐式动画

  我们可以利用事务来关闭隐式动画,使用方式如下,关闭隐式动画之后在修改layer的属性就不会在触发动画了。

  

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[CATransaction begin];
[CATransaction setDisableActions:YES];//关闭动画行为 UITouch *touch = [touches anyObject];
self.testLayer.position = [touch locationInView:self.view];//修改位置的隐式动画
CGFloat WH = arc4random_uniform();
if (WH < ) {
WH += ;
}
self.testLayer.bounds = CGRectMake(, , WH, WH);
[CATransaction commit];
}

效果:

  

  4.2.1 利用事务统一设置动画参数

  可以统一设置的参数有以下四种

kCATransactionAnimationDuration //动画时长
kCATransactionDisableActions //关闭动画行为
kCATransactionAnimationTimingFunction //动画时间曲线
kCATransactionCompletionBlock //动画完成block

  我们以统一设置动画时长为例,以下代码统一设置动画时长为5s

  

[CATransaction begin];

    [CATransaction setValue:[NSNumber numberWithFloat:.f]
forKey:kCATransactionAnimationDuration]; CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
CGPoint fromPoint = CGPointMake(, );
positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];
positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(, )];
positionAnimation.fillMode = kCAFillModeForwards;
positionAnimation.removedOnCompletion = NO; [positionAnimation setValue:@"位置动画" forKey:@"animationName"];//添加属性名 [self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"]; [CATransaction commit];

以上就是Core Animation的基本使用了,你可以在这里下载代码

本文个人原创,转载请注明出处 (http://www.cnblogs.com/pretty-guy/p/8259657.html)

下一篇文章讲Core Animation 与 CAShapeLayer组合起来使用

       

  

iOS 动画篇 之 Core Animation (一)的更多相关文章

  1. iOS 动画效果:Core Animation & Facebook's pop

    本文转载至 http://www.cocoachina.com/ios/20151223/14739.html 感谢原创作者分享 前言相信很多人对实现 iOS 中的动画效果都特别头疼,往往懒得动手,功 ...

  2. iOS 动画篇 (二) CAShapeLayer与CoreAnimation结合使用

    接上一篇博客 iOS 动画篇(一) Core Animation CAShapeLayer是CALayer的一个子类,使用这个类能够很轻易实现曲线的动画. 先来一个折线动画效果: 示例代码: //1. ...

  3. iOS动画篇:核心动画

    转:http://www.cocoachina.com/ios/20160517/16290.html 基本概念 1.什么是核心动画 Core Animation(核心动画)是一组功能强大.效果华丽的 ...

  4. 动画基础--基于Core Animation(3)

    参考:https://zsisme.gitbooks.io/ios-/content/ 前面的文章动画基础--基于Core Animation(1),动画基础--基于Core Animation(2) ...

  5. 动画基础--基于Core Animation(2)

    参考:https://zsisme.gitbooks.io/ios-/content/ 前面的文章动画基础--基于Core Animation(1)提到了图层的基本概念以及可动画参数几何学等知识. 本 ...

  6. 动画基础--基于Core Animation(1)

    1.简介 上一篇文章[New learn]动画-基于UIView了解到了一些直接由UIView这个在UIKIT提供的类中提供的一些动画方法. 使用UIView的动画特性已经能够满足我们很多的需求,它是 ...

  7. iOS动画篇:UIView动画

    iOS的动画效果一直都很棒很,给人的感觉就是很炫酷很流畅,起到增强用户体验的作用.在APP开发中实现动画效果有很多种方式,对于简单的应用场景,我们可以使用UIKit提供的动画来实现. UIView动画 ...

  8. iOS 动画篇 (三) CADisplayLink与CoreGraphics实现动画

    本文主要介绍利用CoreGraphics和CADisplayLink来实现一个注水动画.来一个效果图先: 在介绍注水动画前,先介绍利用CoreGraphics实现进度条的绘制. 一.扇形进度绘制 效果 ...

  9. iOS动画1 — UIView动画

    iOS动画基础是Core Animation核心动画.Core Animation是iOS平台上负责图形渲染与动画的基础设施.由于核心动画的实现比较复杂,苹果提供了实现简单动画的接口—UIView动画 ...

随机推荐

  1. 从源代码到Runtime发生的重排序

     源代码和Runtime时执行的代码很可能不一样,这是因为编译器.处理器常常会为了追求性能对改变执行顺序.然而改变顺序执行很危险,很有可能使得运行结果和预想的不一样,特别是当重排序共享变量时.  从源 ...

  2. javascript变量:全局?还是局部?这个得注意

    在JS中.是没有块级作用域的 举两个个样例: if语句块:     if (true){ var name='Ling'; } alert(name); 输出:Ling for语句块; for(var ...

  3. 设置Eclipse的workspace路径

    首次启动Eclipse/MyEclipse时, 会弹出"Workspace Launcher"对话框, 提示设置Workspace路径. 设定好路径后, 若勾选了"Use ...

  4. 赵雅智_Swift(1)_swift简单介绍及类型

    Swift 是 iOS 和 OS X 应用开发的一门新语言. 假设你有 C 或者 Objective-C 开发经验, Swift 的非常多内容都是你熟悉的. Swift 的类型是在 C 和 Objec ...

  5. vector删除元素与清除内存空洞

    问题:stl中的vector容器经常造成删除假象,这对于c++程序猿来说是极其讨厌的,<effective stl>大师已经将之列为第17条,使用交换技巧来修整过剩容量. 内存空洞这个名词 ...

  6. TortoiseSVN的安装和使用

    TortoiseSVN是windows平台下Subversion的免费开源client. 一般我们都是先讲讲server的配置.然后再讲client的使用,可是在TortoiseSVN上.却能够反过来 ...

  7. nat的翻译类型(3)--端口地址转换

    目的:在1.1 1.2 1.3 三台内网的服务器访问外网的服务器(202.1.1.2)时,将内网ip转换为外网ip. 1.设置内网三台服务器的Ip ,网关,以及外网服务器的ip网关 分别为:192.1 ...

  8. java并发编程的艺术——第一章总结

    并发编程的挑战 1.1上下文切换 1.2死锁 1.3资源限制的挑战 1.4本章小结 1.1上下文切换 1.1.1多线程一定快吗 1.1.2测试上下文切换次数和时长 1.1.3如何减少上下文切换 1.1 ...

  9. redis的set类型

    1.简单描述 redis的set类型是string类型数值的无序集合.set元素最大可以包含2的32次方减1个元素.由于set类型是通过hash table实现(旧版本是这样,新版本不确定是不是改用了 ...

  10. python self

    Python要self的理由 Python的类的方法和普通的函数有一个很明显的区别,在类的方法必须有个额外的第一个参数(self),但在调用这个方法的时候不必为这个参数赋值(显胜于隐的引发). Pyt ...