博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS 动画篇(一) Core Animation
阅读量:6946 次
发布时间:2019-06-27

本文共 14230 字,大约阅读时间需要 47 分钟。

  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(0, 0, 100, 100);        [self.view.layer addSublayer:tempLayer];        tempLayer;    });

 

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

  3.1 CABasicAnimation

  使用代码示例

- (void)touchesBegan:(NSSet
*)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 = 1.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
*)touches withEvent:(UIEvent *)event{ UITouch *touch = [touches anyObject]; self.testLayer.position = [touch locationInView:self.view];//修改位置的隐式动画 CGFloat WH = arc4random_uniform(100); if (WH < 20) { WH += 50; } self.testLayer.bounds = CGRectMake(0, 0, WH, WH); UIColor *color = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 green:arc4random_uniform(255) / 255.0 blue:arc4random_uniform(255) / 255.0 alpha:1.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(0, 0, 50, 50);        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 = 3.f;        return moveAnimation;}- (void)touchesBegan:(NSSet
*)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(0, 0, 30, 30);    self.testLayer.cornerRadius = 30 / 2;        //设置x轴方向的缩放动画    CAKeyframeAnimation *xScaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];    xScaleAnimation.values = @[@1, @0.9, @1, @1.1, @0.9, @1];    xScaleAnimation.duration = 3.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.1, @0.8, @1, @0.9];    yScaleAnimation.duration = 3.f;    yScaleAnimation.repeatCount = CGFLOAT_MAX;    yScaleAnimation.removedOnCompletion = NO;    yScaleAnimation.fillMode = kCAFillModeForwards;        //设置x轴方向的移动动画    CAKeyframeAnimation *xTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];    xTranslationAnimation.values = @[@0, @5, @(-5), @0, @5, @0];    xTranslationAnimation.duration = 3.f;    xTranslationAnimation.repeatCount = CGFLOAT_MAX;    xTranslationAnimation.removedOnCompletion = NO;    xTranslationAnimation.fillMode = kCAFillModeForwards;        //设置y轴方向的移动动画    CAKeyframeAnimation *yTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];    yTranslationAnimation.values = @[@0, @5, @1, @-5, @0];    yTranslationAnimation.duration = 3.f;    yTranslationAnimation.repeatCount = CGFLOAT_MAX;    yTranslationAnimation.removedOnCompletion = NO;    yTranslationAnimation.fillMode = kCAFillModeForwards;        //组动画    CAAnimationGroup *groupAnimation = [[CAAnimationGroup alloc] init];    groupAnimation.animations = @[xScaleAnimation, yScaleAnimation, xTranslationAnimation, yTranslationAnimation];//将所有动画添加到动画组    groupAnimation.duration = 3.f;    groupAnimation.repeatCount = CGFLOAT_MAX;    groupAnimation.removedOnCompletion = NO;    groupAnimation.fillMode = kCAFillModeForwards;        [self.testLayer addAnimation:groupAnimation forKey:@"groupAnimation"];}

  3.5 CATransition(过渡动画)

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

  对于具体的解释见代码:

- (void)touchesBegan:(NSSet
*)touches withEvent:(UIEvent *)event{ CATransition *transition = [CATransition animation]; transition.startProgress = 0;//开始进度 transition.endProgress = 1;//结束进度 transition.type = kCATransitionReveal;//过渡类型 transition.subtype = kCATransitionFromLeft;//过渡方向 transition.duration = 1.f; UIColor *color = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 green:arc4random_uniform(255) / 255.0 blue:arc4random_uniform(255) / 255.0 alpha:1.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(0, 0, 50, 50);        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 = 3.f;        return moveAnimation;}- (void)touchesBegan:(NSSet
*)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 
@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(100, 100);    positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];    positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)];    positionAnimation.duration = 3.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
*)touches withEvent:(UIEvent *)event{ [CATransaction begin]; [CATransaction setDisableActions:YES];//关闭动画行为 UITouch *touch = [touches anyObject]; self.testLayer.position = [touch locationInView:self.view];//修改位置的隐式动画 CGFloat WH = arc4random_uniform(100); if (WH < 20) { WH += 50; } self.testLayer.bounds = CGRectMake(0, 0, WH, WH); [CATransaction commit];}

效果:

  

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

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

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

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

  

[CATransaction begin];        [CATransaction setValue:[NSNumber numberWithFloat:5.f]                     forKey:kCATransactionAnimationDuration];        CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];    CGPoint fromPoint = CGPointMake(100, 100);    positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];    positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)];    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组合起来使用

       

 

  

转载于:https://www.cnblogs.com/pretty-guy/p/8259657.html

你可能感兴趣的文章
ES6基础-解构赋值
查看>>
html转义字符
查看>>
C++ 简单的日志类
查看>>
VirtualBox不显示64bit版本的iso
查看>>
vim缩进
查看>>
UVA 10837 A Research Problem
查看>>
NOIP模拟2
查看>>
java自定义注解
查看>>
选择排序
查看>>
【下一代核心技术DevOps】:(六)Rancher集中存储及相关应用
查看>>
关于AFNetWorking3.0内存泄漏的问题
查看>>
简单的一个布局CSS+DIV
查看>>
面试时要懂得说的黄金五条
查看>>
字王4K云字库入驻github
查看>>
UVa10561 Treblecross
查看>>
JS调用命令实现F11全屏
查看>>
a标签href无值,点击刷新页面解决办法
查看>>
Arm开发板+Qt学习之路
查看>>
unknown local index 'index_name' in search request
查看>>
看视频学编程之C#中的类
查看>>