美文网首页
iOS-CoreAnimation核心动画

iOS-CoreAnimation核心动画

作者: Imkata | 来源:发表于2019-11-08 17:30 被阅读0次

    学完上一篇iOS-CAlayer之后,我们就可以学习一下核心动画了。

    一. CoreAnimation简介

    1. 层级

    首先看了解一下层级: 框架层级.png

    其中上面的层级可以访问下面的层级,可以看出CoreAnimation也会使用CoreGraphics里面的一些东西。

    2. CoreAnimation可以做什么?

    进入CoreAnimation.h文件

    #include <QuartzCore/CABase.h>
    #include <QuartzCore/CATransform3D.h>
    
    #ifdef __OBJC__
    #import <Foundation/Foundation.h>
    #import <QuartzCore/CAAnimation.h>
    #import <QuartzCore/CADisplayLink.h>  //定时器相关
    #import <QuartzCore/CAEAGLLayer.h>
    #import <QuartzCore/CAEmitterCell.h>
    #import <QuartzCore/CAEmitterLayer.h>  //一些layer层
    #import <QuartzCore/CAGradientLayer.h>
    #import <QuartzCore/CALayer.h>  //我们熟悉的layer层
    #import <QuartzCore/CAMediaTiming.h>
    #import <QuartzCore/CAMediaTimingFunction.h> //时间相关
    #import <QuartzCore/CAReplicatorLayer.h>
    #import <QuartzCore/CAScrollLayer.h>
    #import <QuartzCore/CAShapeLayer.h>
    #import <QuartzCore/CATextLayer.h>
    #import <QuartzCore/CATiledLayer.h>
    #import <QuartzCore/CATransaction.h>
    #import <QuartzCore/CATransform3D.h>   //矩阵转换
    #import <QuartzCore/CATransformLayer.h>
    #import <QuartzCore/CAValueFunction.h>
    #endif
    

    可以发现CoreAnimation不单单包含动画还包含一些其他的东西,具体可看上面注释。

    1. Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。
    2. Core Animation是跨平台的,可以用在Mac OS X和iOS平台。
    3. Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。不阻塞主线程,可以理解为在执行动画的时候还能点击(按钮)。
    4. 要注意的是,Core Animation是直接作用在CALayer上的,并非UIView。

    3. CAAnimation继承关系

    CAAnimation继承图如下: CAAnimation.png
    1. CAPropertyAnimation是CAAnimation的子类,但是不能直接使用,要想创建动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation。
    2. CAMediaTiming是一个协议(protocol)。
    3. 能用的动画类只有4个子类:CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransition。

    部分属性解析:

    其中带*的表示来自CAMediaTiming协议的属性。

    duration: *//动画的持续时间
    repeatCount: *//动画的重复次数
    repeatDuration: *//动画的重复时间
    fillMode: *//决定当前对象在非active时间段的行为,比如动画开始之前,动画结束之后
    beginTime: *//可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
    removedOnCompletion://默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
    timingFunction://速度控制函数,控制动画运行的节奏
    delegate://动画代理
    

    二. CABasicAnimation

    CABasicAnimation是CAPropertyAnimation的子类。

    属性解析:

    fromValue: //keyPath相应属性的初始值
    toValue: //keyPath相应属性的结束值
    

    随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue。

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

    1. CABasicAnimation的简单使用

    比如进行平移、旋转、缩放的动画,代码如下:

    #import "ViewController.h"
    
    @interface ViewController () <CAAnimationDelegate>
    
    @property(nonatomic,strong) CALayer *myLayer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建layer
        CALayer *myLayer=[CALayer layer];
        //设置layer的属性
        myLayer.bounds=CGRectMake(0, 0, 150, 60);
        myLayer.backgroundColor=[UIColor yellowColor].CGColor;
        myLayer.position=CGPointMake(100, 100);
        myLayer.anchorPoint=CGPointMake(0, 0);
        myLayer.cornerRadius=40;
        //添加layer
        self.myLayer=myLayer;
        [self.view.layer addSublayer:myLayer];
    }
    
    //设置动画
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //[self pingyi];
        //[self suofang];
        [self xuanzhuan];
    }
    
    //平移
    - (void)pingyi {
        //1.创建核心动画
        // CABasicAnimation *anima=[CABasicAnimation animationWithKeyPath:<#(NSString *)#>]
        CABasicAnimation *anima=[CABasicAnimation animation];
        
        //1.1告诉系统要执行什么样的动画
        anima.keyPath=@"position";
        //设置通过动画,将layer从哪儿移动到哪儿
        anima.fromValue=[NSValue valueWithCGPoint:CGPointMake(0, 0)];
        anima.toValue=[NSValue valueWithCGPoint:CGPointMake(200, 300)];
        
        //1.2设置动画执行完毕之后不删除动画
        anima.removedOnCompletion=NO;
        //1.3设置保存动画的最新状态
        anima.fillMode = @"forwards";
        //anim.fillMode = kCAFillModeForwards; // 和上面一行一样的
        
        anima.delegate = self;
        NSString *str = NSStringFromCGPoint(self.myLayer.position);
        NSLog(@"执行前:%@",str);
        
        //2.添加核心动画到layer
        [self.myLayer addAnimation:anima forKey:nil];
    }
    
    //缩放
    - (void)suofang {
        //缩放动画可以实现心跳效果
        //创建动画对象
        CABasicAnimation *anim = [CABasicAnimation animation];
        
        //设置属性值
        anim.keyPath = @"transform.scale";
        anim.toValue = @0;
        
        //设置动画执行次数
        anim.repeatCount = MAXFLOAT;
        //设置动画执行时长
        anim.duration = 3;
        //自动反转(怎么样去 怎么样回来)
        anim.autoreverses = YES;
        
        //添加动画
        [self.myLayer addAnimation:anim forKey:nil];
    }
    
    //旋转
    - (void)xuanzhuan {
        //1.创建动画
        CABasicAnimation *anima=[CABasicAnimation animationWithKeyPath:@"transform"];
        //1.1设置动画执行时间
        anima.duration=2.0;
        //1.2修改属性,执行动画
        anima.toValue=[NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2+M_PI_4, 1, 1, 0)];
        //1.3设置动画执行完毕后不删除动画
        anima.removedOnCompletion=NO;
        //1.4设置保存动画的最新状态
        anima.fillMode=kCAFillModeForwards;
        
        //2.添加动画到layer
        [self.myLayer addAnimation:anima forKey:nil];
    }
    
    #pragma mark - CAAnimationDelegate代理
    
    -(void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"开始执行动画");
    }
    
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        //动画执行完毕,打印执行完毕后的position值
        NSString *str = NSStringFromCGPoint(self.myLayer.position);
        NSLog(@"执行后:%@",str);
    }
    @end
    
    平移: 平移.gif 缩放: 缩放.gif 旋转: 旋转.gif

    打印结果:

    2019-11-08 14:21:41.030264+0800 核心动画[10803:9330751] 执行前:{100, 100}
    2019-11-08 14:21:41.031078+0800 核心动画[10803:9330751] 开始执行动画
    2019-11-08 14:21:41.282279+0800 核心动画[10803:9330751] 执行后:{100, 100}
    

    打印position的属性值,验证了,进行平移动画后,图层的属性值还是动画执行前的初始值{100,100},并没有真正被改变为{200,300}。

    2. 贝塞尔曲线

    UIBezierPath是苹果封装的路径,一般作为动画的路径(推荐使用)。

    下面使用贝塞尔曲线和CABasicAnimation实现如下效果: 效果.gif

    代码:

    - (void)animationCaseOne {
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bg-mine.png"]];
        imageView.frame = CGRectMake(100.f, 200.f, 100.f, 100.f);
        imageView.clipsToBounds = YES;
        [self.view addSubview:imageView];
        
        //添加一层浅白色layer
        layer = [CAShapeLayer layer];
        
        //CAShapeLayer占用空间比较少,CAShapeLayer和UIBezierPath是黄金搭档
        /*
         圆心
         半径
         开始角度
         结束角度
         顺时针
         */
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50.f, 50.f) radius:50.f startAngle:-M_PI_2 endAngle:3*M_PI/2 clockwise:YES];
        layer.path = path.CGPath;
        layer.fillColor = [UIColor clearColor].CGColor; //填充色,默认是黑色的
        layer.strokeColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f].CGColor; //绘制色
        //线宽设置为半径100
        layer.lineWidth = 100.f;
        
        [imageView.layer addSublayer:layer];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        //strokeStart单位属性0-1 开始画 结束画
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        anim.duration = 4.f;
        anim.fromValue = @0;
        anim.toValue = @1;
        [layer addAnimation:anim forKey:nil];
    }
    
    下面有另外一个需求,如下实现如下图片: 效果图.png

    有三种方式实现,代码如下,就不解释了

    /*
     shapelayer.fillRule:1、kCAFillRuleEvenOdd 2、kCAFillRulenonzero
     
     nonzero字面意思是“非零”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右穿过射线则计数加1,从右向左穿过射线则计数减1。得出计数结果后,如果结果是0,则认为点在图形外部,否则认为在内部
     evenodd字面意思是“奇偶”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部
     */
    - (void)animationCaseTwoMethod1 {
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bg-mine.png"]];
        imageView.frame = CGRectMake(100.f, 200.f, 100.f, 100.f);
        [self.view addSubview:imageView];
        
        //矩形
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0.f, 0.f, 100.f, 100.f)];
        [path addArcWithCenter:CGPointMake(50.f, 50.f) radius:50.f startAngle:0 endAngle:M_PI*2 clockwise:YES];
        
        layer = [CAShapeLayer layer];
        layer.path = path.CGPath;
        layer.fillRule = kCAFillRuleEvenOdd; //改变判断是不是在圆内的规则
        
        layer.fillColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f].CGColor; //然后画上去
        [imageView.layer addSublayer:layer];
    }
    
    - (void)animationCaseTwoMethod2 {
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bg-mine.png"]];
        imageView.frame = CGRectMake(100.f, 200.f, 100.f, 100.f);
        [self.view addSubview:imageView];
        
        //rct
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0.f, 0.f, 100.f, 100.f)];
        UIBezierPath *pathOne = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50.f, 50.f) radius:50.f startAngle:0 endAngle:M_PI*2 clockwise:YES];
        [path appendPath:[pathOne bezierPathByReversingPath]]; //两条线当成一条线
        
        layer = [CAShapeLayer layer];
        layer.path = path.CGPath;
        
        layer.fillColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f].CGColor; //然后画上去
        [imageView.layer addSublayer:layer];
    }
    
    - (void)animationCaseTwoMethod3 {
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bg-mine.png"]];
        imageView.frame = CGRectMake(100.f, 200.f, 100.f, 100.f);
        [self.view addSubview:imageView];
        
        UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 100.f, 100.f)];
        maskView.backgroundColor = [UIColor colorWithWhite:1.f alpha:0.4f];
        
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0.f, 0.f, 100.f, 100.f)];
        UIBezierPath *pathOne = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50.f, 50.f) radius:50.f startAngle:0 endAngle:M_PI*2 clockwise:YES];
        [path appendPath:[pathOne bezierPathByReversingPath]];
        
        layer = [CAShapeLayer layer];
        layer.path = path.CGPath;
        
        maskView.layer.mask = layer; //layer的遮罩就是我们画出的两条线
        
        [imageView addSubview:maskView];
    }
    

    三. CAKeyframeAnimation

    1. CAKeyframeAnimation也是CApropertyAnimation的子类。
    2. CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。

    属性解析:

    values: //就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
    path: //可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略
    keyTimes: //可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的
    

    说明:CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation。

    代码:

    #import "ViewController.h"
    
    @interface ViewController () <CAAnimationDelegate>
    
    @property (weak, nonatomic) UIView *customView;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
        view.backgroundColor = [UIColor orangeColor];
        self.customView = view;
        [self.view addSubview:view];
    }
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //1.创建核心动画
        CAKeyframeAnimation *keyAnima=[CAKeyframeAnimation animation];
        //平移
        keyAnima.keyPath=@"position";
        //1.1告诉系统要执行什么动画
        NSValue *value1=[NSValue valueWithCGPoint:CGPointMake(100, 100)];
        NSValue *value2=[NSValue valueWithCGPoint:CGPointMake(200, 100)];
        NSValue *value3=[NSValue valueWithCGPoint:CGPointMake(200, 200)];
        NSValue *value4=[NSValue valueWithCGPoint:CGPointMake(100, 200)];
        NSValue *value5=[NSValue valueWithCGPoint:CGPointMake(100, 100)];
        keyAnima.values=@[value1,value2,value3,value4,value5];
         //1.2设置动画执行完毕后,不删除动画
        keyAnima.removedOnCompletion=NO;
        //1.3设置保存动画的最新状态
        keyAnima.fillMode=kCAFillModeForwards;
        //1.4设置动画执行的时间
        keyAnima.duration=4.0;
         //1.5设置动画的节奏
        keyAnima.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
        //设置代理,开始—结束
        keyAnima.delegate=self;
        //2.添加核心动画
        [self.customView.layer addAnimation:keyAnima forKey:nil];
    }
    
    -(void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"开始动画");
    }
    
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        NSLog(@"结束动画");
    }
    @end
    
    动画效果: 位移.gif

    除了使用values也可以使用path,让动画跟着路径移动。上面的动画也可以使用path,代码如下:

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //1.创建动画对象
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
        
        anim.duration = 2;
        
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(50, 50)];
        [path addLineToPoint:CGPointMake(300, 50)];
        [path addLineToPoint:CGPointMake(300, 400)];
        
        anim.keyPath =  @"position";
        anim.path = path.CGPath;
        
        //设置代理
        anim.delegate=self;
    
        self.customView.layer.anchorPoint = CGPointZero;
        [self.customView.layer addAnimation:anim forKey:nil];
    }
    
    动画效果: path.gif

    也可以设置弧度实现抖动效果,如下:

    #define angle2Radian(angle) ((angle) / 180.0 * M_PI)
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //1.创建核心动画
        CAKeyframeAnimation *keyAnima=[CAKeyframeAnimation animation];
        keyAnima.keyPath=@"transform.rotation";
        //设置动画时间
        keyAnima.duration=0.1;
        //设置图标抖动弧度
        //把度数转换为弧度  度数/180*M_PI
        keyAnima.values=@[@(-angle2Radian(4)),@(angle2Radian(4)),@(-angle2Radian(4))];
        //设置动画的重复次数(设置为最大值)
        keyAnima.repeatCount=MAXFLOAT;
        keyAnima.fillMode=kCAFillModeForwards;
        keyAnima.removedOnCompletion=NO;
        //2.添加动画
        [self.customView.layer addAnimation:keyAnima forKey:nil];
    }
    
    效果图: 抖动.gif

    最后不要忘记移除动画:

    [self.customView.layer removeAnimationForKey:@"wendingding"];
    

    示例:

    下面再讲一个如何使用关键帧动画和贝塞尔曲线实现如下动画

    关键帧动画.gif

    代码:

    #import "EOCKeyFrameAnimationViewController.h"
    
    @interface EOCKeyFrameAnimationViewController ()<CAAnimationDelegate> {
        CAShapeLayer *shapeLayer;
        UIBezierPath *path;
    }
    
    @end
    
    @implementation EOCKeyFrameAnimationViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor lightGrayColor];
        
        CAShapeLayer *pathLayer = [CAShapeLayer layer];
        path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(40.f, 175.f)];
        [path addCurveToPoint:CGPointMake(300.f, 175.f) controlPoint1:CGPointMake(50.f, 40.f) controlPoint2:CGPointMake(200.f, 300.f)];
        pathLayer.path = path.CGPath;
        pathLayer.lineWidth = 2.f;
        pathLayer.fillColor = [UIColor clearColor].CGColor;
        
        [pathLayer setStrokeColor:[UIColor redColor].CGColor];
        [self.view.layer addSublayer:pathLayer];
        
        shapeLayer = [CAShapeLayer layer];
        shapeLayer.contents = (__bridge id)[UIImage imageNamed:@"plane.png"].CGImage;
        shapeLayer.bounds = CGRectMake(0.f, 0.f, 50.f, 50.f);
        shapeLayer.position = CGPointMake(40.f, 175.f);
        [self.view.layer addSublayer:shapeLayer];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        
        anim.duration = 4.f;
        anim.repeatCount = 1;
        anim.rotationMode = kCAAnimationRotateAuto;
        // anim.autoreverses = YES;
        anim.calculationMode = kCAAnimationCubicPaced;  //如果用keyTimes和values,会卡顿着走,用这个平滑一些
        anim.path = path.CGPath;
        
        anim.keyTimes = @[@0.4, @0.7, @0.9];
        anim.values = @[[NSValue valueWithCGPoint:CGPointMake(60, 200)], [NSValue valueWithCGPoint:CGPointMake(100, 200)], [NSValue valueWithCGPoint:CGPointMake(140, 200)]];
        
        [shapeLayer addAnimation:anim forKey:nil];
    }
    @end
    

    四. CAAnimationGroup 动画组

    CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。

    属性解析:

    animations://用来保存一组动画对象的NSArray
    

    默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。

    例如,平移、旋转、缩放作为一组动画一起执行,代码如下:

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
         // 平移动画
         CABasicAnimation *a1 = [CABasicAnimation animation];
         a1.keyPath = @"transform.translation.y";
         a1.toValue = @(200);
         // 缩放动画
         CABasicAnimation *a2 = [CABasicAnimation animation];
         a2.keyPath = @"transform.scale";
         a2.toValue = @(0.3);
         // 旋转动画
         CABasicAnimation *a3 = [CABasicAnimation animation];
         a3.keyPath = @"transform.rotation";
         a3.toValue = @(M_PI_2);
    
         // 组动画
         CAAnimationGroup *groupAnima = [CAAnimationGroup animation];
    
         groupAnima.animations = @[a1, a2, a3];
    
         //设置组动画的时间
         groupAnima.duration = 2;
    //     groupAnima.fillMode = kCAFillModeForwards;
    //     groupAnima.removedOnCompletion = NO;
    
         [self.customView.layer addAnimation:groupAnima forKey:nil];
    }
    
    效果如下: CAAnimationGroup.gif

    五. CATransition

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

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

    属性解析:

    type://动画过渡类型
    subtype://动画过渡方向
    startProgress://动画起点(在整体动画的百分比)
    endProgress://动画终点(在整体动画的百分比) 
    

    合法的转场动画类型有:

    fade://默认,faker淡出,layer淡入
    moveIn://layer移入覆盖faker
    push://layer推入,faker推出
    reveal://覆盖在layer上面的faker被移出
    

    私有(被苹果ban了,不建议直接使用):

    cube://立方体旋转,layer将会在呈现的面,faker在不可见的面
    suckEffect://覆盖在layer上面的faker被抽离
    oglFlip://将背面的layer翻转到前面,faker翻转到背面、、
    rippleEffect://伴随着水面波动动画,faker淡出,layer淡入
    pageCurl://翻到下一页,faker被翻走,呈现layer
    pageUnCurl://翻回上一页,layer被翻回并覆盖faker
    cameraIrisHollowOpen://下面这两个是特殊的。镜头开,同时呈现部分为透明,而不是layer
    cameraIrisHollowClose://类似上面,镜头关
    

    subtype:

    //4个子类型,表示上左下右4个转场动画方向
    fromTop
    fromLeft
    fromBottom
    fromRight
    

    示例:

    利用转场动画做立体翻页效果,代码如下:

    - (void)leftBtnClicked {
        self.index--;
        if (self.index<1) {
            self.index=5;
        }
        self.iconView.image=[UIImage imageNamed: [NSString stringWithFormat:@"%d.png",self.index]];
        [self addAnimWithLeft:YES];
    }
    
    - (void)rightBtnClicked {
        self.index++;
        if (self.index>5) {
            self.index=1;
        }
        self.iconView.image=[UIImage imageNamed: [NSString stringWithFormat:@"%d.png",self.index]];
        [self addAnimWithLeft:NO];
    }
    
    - (void)addAnimWithLeft:(BOOL)isleft {
        //创建核心动画
        CATransition *ca = [CATransition animation];
        //告诉要执行什么动画
        //设置过度效果
        ca.type = @"cube";
        //设置动画的过度方向(向左)
        ca.subtype = isleft ? kCATransitionFromLeft : kCATransitionFromRight;
        //设置动画的时间
        ca.duration = 2.0;
        //设置动画的起始位置  比如翻页功能就能设置从哪里开始翻页
        //    ca.startProgress = 0.3;
        //    //设置动画的结束位置
        //    ca.endProgress = 0.5;
        //添加动画
        [self.iconView.layer addAnimation:ca forKey:nil];
    }
    
    动画效果: 立体翻页.gif

    其实利用UIView的动画也可以做转场动画,还简单一点呢,如下:

    [UIView transitionWithView:self.iconView duration:0.5 options:UIViewAnimationOptionTransitionCurlUp animations:^{
            //转场代码
            self.index = self.index + 1;
            if (self.index > 5) {
                self.index = 1;
            }
            NSString *imageName = [NSString stringWithFormat:@"%d.png",self.index];
            self.iconView.image = [UIImage imageNamed:imageName];
        } completion:^(BOOL finished) {
        }];
    
    动画效果: UIView转场动画.gif

    六. 动画时间

    下面使用一个小案例介绍下动画时间,主要掌握这三个参数:beginTime speed timeOffset

    效果图: 动画时间.gif

    代码:

    #import "EOCCAMediaTimingViewController.h"
    
    @interface EOCCAMediaTimingViewController ()<CAAnimationDelegate> {
        
        CAShapeLayer *shapeLayer;
        int i; //是否暂停
        CFTimeInterval pausedTime; //暂停时间
        
    }
    
    @end
    
    @implementation EOCCAMediaTimingViewController
    
    - (void)viewDidLoad {
        
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor lightGrayColor];
        i = 0;
        
        //1.跟时间相关的要掌握:beginTime  speed  timeOffset
        
        shapeLayer = [CAShapeLayer layer];
        shapeLayer.contents = (__bridge id)[UIImage imageNamed:@"plane.png"].CGImage;
        shapeLayer.speed = 0.f; //默认1
        shapeLayer.bounds = CGRectMake(0.f, 0.f, 50.f, 50.f);
        shapeLayer.position = CGPointMake(25.f, 100.f);
        
        [self.view.layer addSublayer:shapeLayer];
        
        CAShapeLayer *lineLayer = [CAShapeLayer layer];
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(25.f, 100.f)];
        [path addLineToPoint:CGPointMake(300.f, 300.f)];
        lineLayer.lineWidth = 2.f;
        lineLayer.strokeColor = [UIColor redColor].CGColor;
        lineLayer.path = path.CGPath;
        [self.view.layer addSublayer:lineLayer];
        
        //CAKeyframeAnimation使用UIBezierPath当做路径,优先使用路径而不是values
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        anim.duration = 10.f;
    //    anim.beginTime = CACurrentMediaTime() + 5.f;  //让动画推迟5s执行,默认马上执行
        anim.path = path.CGPath;
    //    anim.autoreverses = YES; //动画是否原路返回回到起点,默认是NO
    //    anim.timeOffset = 3.f;
        [shapeLayer addAnimation:anim forKey:nil];
        
        //新建UISlider, 改变timeOffset
        UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(100.f, 300.f, 200.f, 30.f)];
        slider.minimumValue = 0.f;
        slider.maximumValue = 10.f;
        [slider addTarget:self action:@selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
        [self.view addSubview:slider];
        
    }
    
    - (void)sliderAction:(UISlider *)slider {
        shapeLayer.timeOffset = slider.value;
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // t(本层的时间) = (tp(父层的时间) - begin) * speed + timeOffset
        if (0 == i) {   //暂停
            pausedTime = [shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil];
            shapeLayer.speed = 0.f;
            shapeLayer.timeOffset = pausedTime;
        } else if (1 == i) {      //启动
            //  t = (tp - begin) * speed + offset
            shapeLayer.speed = 1.f;
            shapeLayer.beginTime = CACurrentMediaTime();
        }
        I++;
        i %= 2;
    }
    @end
    

    七. 综合动画案例

    效果图: 综合案例.gif

    代码:

    #import "EOCAnimationCaseView.h"
    
    @interface EOCAnimationCaseView () {
            CAShapeLayer *topLineLayer;
            CAShapeLayer *leftLineLayer;
            CAShapeLayer *bottomLineLayer;
            CAShapeLayer *rightLineLayer;
    }
    @end
    
    @implementation EOCAnimationCaseView
    
    #define firstColor    [UIColor colorWithRed:90 / 255.0 green:200 / 255.0 blue:200 / 255.0 alpha:1.0]
    #define secondColor   [UIColor colorWithRed:250 / 255.0 green:85 / 255.0 blue:78 / 255.0 alpha:1.0]
    #define thirdColor    [UIColor colorWithRed:92 / 255.0 green:201 / 255.0 blue:105 / 255.0 alpha:1.0]
    #define fourthColor   [UIColor colorWithRed:253 / 255.0 green:175 / 255.0 blue:75 / 255.0 alpha:1.0]
    
    - (instancetype)init
    {
        if (self = [super init]) {
            self.frame = CGRectMake(100.0f, 200.0f, 80.0f, 80.0f);
            self.backgroundColor = [UIColor lightGrayColor];
            [self createLayer];
        }
        return self;
    }
    
    - (void)createLayer {
        //新建四条不同颜色的线
        CGPoint topPoint = CGPointMake(3*80.f/4, 5.f);
        CGPoint leftPoint = CGPointMake(5.f, 80.f/4);
        CGPoint bottomPoint = CGPointMake(80.f/4, 80.f-5.f);
        CGPoint rightPoint = CGPointMake(80.f-5.f, 3*80.f/4);
        
        CGFloat lineLength = 80.f - 2*5.f;
        
        topLineLayer = [self createLineLayer:topPoint toPoint:CGPointMake(topPoint.x , topPoint.y + lineLength) withColor:firstColor];
        leftLineLayer = [self createLineLayer:leftPoint toPoint:CGPointMake(leftPoint.x + lineLength , leftPoint.y) withColor:secondColor];
        bottomLineLayer = [self createLineLayer:bottomPoint toPoint:CGPointMake(bottomPoint.x , bottomPoint.y - lineLength) withColor:thirdColor];
        rightLineLayer = [self createLineLayer:rightPoint toPoint:CGPointMake(rightPoint.x - lineLength , rightPoint.y) withColor:fourthColor];
        
        [self.layer addSublayer:topLineLayer];
        [self.layer addSublayer:leftLineLayer];
        [self.layer addSublayer:bottomLineLayer];
        [self.layer addSublayer:rightLineLayer];
        
        self.transform = CGAffineTransformMakeRotation(-M_PI/6);
    }
    
    - (CAShapeLayer *)createLineLayer:(CGPoint)fromPoint toPoint:(CGPoint)toPoint withColor:(UIColor *)color {
        
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        [path moveToPoint:fromPoint];
        [path addLineToPoint:toPoint];
        
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.lineCap = kCALineCapRound;
        layer.strokeColor = color.CGColor;
        layer.lineWidth = 10.f;
        layer.opacity = 0.8f;
        
        layer.path = path.CGPath;
        return layer;
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [self createAnimations];
    }
    
    - (void)createAnimations {
        
        //自身旋转
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        anim.toValue = @(M_PI*2-M_PI/6);
        anim.duration = 3.5f;
        anim.repeatDuration = 7.f;
        [self.layer addAnimation:anim forKey:nil];
        
        //让线条缩短
        [self createLineLayerAnim:topLineLayer pointX:0.f pointY:15.f];
        [self createLineLayerAnim:leftLineLayer pointX:15.f pointY:0.f];
        [self createLineLayerAnim:bottomLineLayer pointX:0.f pointY:-15.f];
        [self createLineLayerAnim:rightLineLayer pointX:-15.f pointY:0.f];
        
        //此时停止了旋转,让线条变长
        CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        anim1.fillMode = kCAFillModeForwards;
        anim1.removedOnCompletion = NO;
        anim1.beginTime = CACurrentMediaTime() + 7.f;
        anim1.fromValue = @0;
        anim1.toValue = @1;
        anim1.duration = 3.f;
        [topLineLayer addAnimation:anim1 forKey:nil];
        [leftLineLayer addAnimation:anim1 forKey:nil];
        [bottomLineLayer addAnimation:anim1 forKey:nil];
        [rightLineLayer addAnimation:anim1 forKey:nil];
        
        //self变大
        CAKeyframeAnimation *anim3 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
        anim3.duration = 8.0f;
        anim3.beginTime = CACurrentMediaTime()+7.0f;
        NSValue *fromValue = @1;
        NSValue *toValue = @1.2;
        
        anim3.keyTimes = @[@0.1, @0.4, @0.5];
        anim3.values = @[fromValue, toValue, fromValue];
        [self.layer addAnimation:anim3 forKey:nil];
    }
    
    - (void)createLineLayerAnim:(CAShapeLayer *)layer pointX:(CGFloat)pointx pointY:(CGFloat)pointy {
        
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        anim.fillMode = kCAFillModeForwards;
        anim.removedOnCompletion = NO;
        anim.fromValue = @1;
        anim.toValue = @0;
        anim.duration = 3.f;
        [layer addAnimation:anim forKey:nil];
        
        //让这个点前后有位移
        CAKeyframeAnimation *anim1 = [CAKeyframeAnimation animation];
        anim1.keyPath = @"transform";  //课堂上这里是transform.translation 所以没效果,因为我的fromValue值设置的就是transform的值
        anim1.repeatDuration = 4.0f;
        anim1.beginTime = CACurrentMediaTime()+3.0f;
        anim1.duration = 1.0f;
        NSValue *fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 0, 0.f)];
        NSValue *toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(pointx, pointy, 0.f)];
        anim1.values = @[fromValue, toValue, fromValue, toValue, fromValue];
        [layer addAnimation:anim1 forKey:nil];
    }
    @end
    

    Demo地址:核心动画
    Demo地址:高级动画二

    相关文章

      网友评论

          本文标题:iOS-CoreAnimation核心动画

          本文链接:https://www.haomeiwen.com/subject/jluibctx.html