• iOS_核心动画(二)


    目  录:

    一、Core Animation开发步骤

    二、Core Animation的继承结构

    三、CAAnimation常用的属性

    四、CAPropertyAnimation(属性动画)

    五、CABasicAnimation(基本动画)

    六、CAKeyframeAnimation(关键帧动画)

    七、CAAnimationGroup(动画组)

    八、CATransition(转场动画)

      上节中分享了核心动画的基础部分CALayer,知道了核心动画的操作对象不是UIView,而是基于CALayer。今天来看下Core Animation如何实现动画效果的。

      Core Animation是一组非常强大的动画处理API,使用它能我们可以做出非常炫丽的动画效果,而且往往是事半功倍!不过在iOS 7之前使用它需要先添加QuartzCore.framework和引入对应的框架<QuartzCore/QuartzCore.h>。Core Aaimation在创建动画上不仅简单,而且具有更好的性能,原因有两个:
      (1)动画在单独的线程中完成,不会阻塞主线程。
      (2)动画只会重绘界面上变化的部分(部分刷新)。
    一、Core Animation开发步骤
      Core Animation开发步骤,大致划分两步:
      1.初始化一个动画对象(CAAnimation)并设置一些动画相关属性
      (1)CALayer中很多属性都可以通过CAAnimation实现动画效果,包括:opacity、position、transform、bounds、contents等(可以在API文档中搜索:CALayer Animatable Properties)
      2.添加动画对象到层(CALayer)中,开始执行动画
      (1)通过调用CALayer的addAnimation:forKey增加动画到层(CALayer)中,这样就能触发动画了。通过调用removeAnimationForKey可以停止层中的动画。
      (2)Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。
    二、Core Animation的继承结构
      Core Animation动画的核心时CALayer,每个UIView都有自己的CALayer,而且每个CALayer都可不断地添加子CALayer,CALayer所在的CALayer称为父CALyaer,CALayer的这种组织方式被称为Layer Tree(各Layer之间的机构就像一棵树)。
      图为:CAAnimation继承结构

      

      注意:图中的黑色虚线代表“继承”某个类,红色虚线代表“遵守”某个协议。

      从上图可以看出CAAnimation时所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,如果设置动画应该使用它具体的子类。

      (1)CAAnimation:它是所有动画类的基类,它实现了CAMediaTiming协议,提供了动画的持续时间、速度和重复计数等。CAAnimation还实现了CAAction协议,该协议为CALayer动画触发的动作提供标准化响应。

      (2)CATransition:可以通过预置的过渡效果来控制CALayer层的过渡动画。

      (3)CAPropertyAnimation:它代表一个属性动画,可通过+animationWithKeyPath:类方法来创建属性动画实例(程序一般创建该子类的实例),该方法需要指定一个CALayer支持动画的属性,然后通过它的子类控制CALayer的动画属性慢慢睇改变,即可实现CALayer动画。

      (4)CBasicAnimation:简单地控制CALayer层的属性慢慢改变,从而实现动画效果。很多CALayer层的属性值的修改默认会执行这个动画类。比如大小、透明度、颜色等属性。

      (5)CAKeyframeAnimation:支持关键帧的属性动画,该动画的最大特点在于可通过values属性指定多个关键帧,通过多个关键帧可以指定动画的各阶段的关键值。

      (6)CAAnimationGroup:用于将多个动画组合在一起执行。

    三、CAAnimation常用的属性(红色代表来自CAMediaTiming协议的属性)

      (1) duration:动画的持续时间

      (2) repeatCount:重复的次数,无线循环可以设置HUGE_VALF或者MAXFLOAT

      (3)repeatDuration:重复时间

      (4)removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards(产生一个动画将CALayer移动到另外一个位置,而CALayer的实际位置却没有改变,仍然在动画开启前的位置)

       (5)beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间

    默认值为0,无延迟

       (6)delegate:动画代理
       (7) fillMode:动画填充模式(要想fillMode有效,需要设置removedOnCompletion = NO),它的属性值是一个枚举类型的,如下:
        –kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
        –kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
        –kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
        –kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
      (8)速度控制函数(CAMediaTimingFunction)
        –kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
        –kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
        –kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
        –kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
     四、CAPropertyAnimation(属性动画)
      是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:
      (1)CABasicAnimation(基本动画)
      (2)CAKeyframeAnimation(关键帧动画)
      属性说明:
      –keyPath:通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@“position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果。
    五、CABasicAnimation(基本动画)
      CABasicAnimation有两个属性:
      –fromValue:keyPath相应属性的初始值(起始点)
      –toValue:keyPath相应属性的结束值(结束点)
      动画过程说明:
      –随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue。
      –keyPath内容是CALayer的可动画Animatable属性
      –如果fillMode=kCAFillModeForwards同时removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。
      案例分析:
    #import "ViewController.h"
    
    @interface ViewController ()
    @property(strong,nonatomic)CALayer *subLayer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建子层
        self.subLayer = [CALayer layer];
        self.subLayer.bounds = CGRectMake(0, 0, 100, 100);
        self.subLayer.position = CGPointMake(100, 100);
        self.subLayer.cornerRadius = 50;
        self.subLayer.backgroundColor = [[UIColor greenColor]CGColor];
    
        
        [self.view.layer addSublayer:self.subLayer];
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
    
        tap.numberOfTapsRequired = 1;
        //接受几个手指响应的事件
        tap.numberOfTouchesRequired = 1;
        [self.view addGestureRecognizer:tap];
        
    }
    -(void)tap:(UITapGestureRecognizer *)sender
    {
        CGPoint location = [sender locationInView:self.view];
        CABasicAnimation *basic = [[CABasicAnimation alloc]init];
        basic.duration = 3.0;//动画时长
        basic.keyPath = @"position";
        basic.removedOnCompletion = NO;
        basic.fillMode = kCAFillModeForwards;
        basic.delegate = self;
        //将动画的结束位置移动到点击的坐标处
        basic.toValue = [NSValue valueWithCGPoint:location];
        [self.subLayer addAnimation:basic forKey:@"basic"];
        
    }
    - (IBAction)pauseClicked:(UIButton *)sender
    {
        if (sender.tag == 0)
        {
            //动画暂停
            [self animationPause];
            sender.tag = 1;
        }
        else
        {
            //动画恢复
            [self animationResume];
            sender.tag = 0;
        }
    }
    //动画暂停
    -(void)animationPause
    {
        //获取当前暂停的时间段
        CFTimeInterval pauseTime = [self.subLayer convertTime:CACurrentMediaTime() fromLayer:nil];
        NSLog(@"pausetTime:%f",pauseTime);
        
        //将速度降为0,动画停止
        self.subLayer.speed = 0.0;
        //保存暂停时间
        self.subLayer.timeOffset = pauseTime;
    }
    -(void)animationResume
    {
        CFTimeInterval pauseTime = self.subLayer.timeOffset;
        self.subLayer.timeOffset = pauseTime;
        self.subLayer.speed = 1.0;
        self.subLayer.beginTime = 0.0;
        
        //计算开始时间
        CFTimeInterval beginTime = [self.subLayer convertTime:CACurrentMediaTime() fromLayer:nil]-pauseTime;
        self.subLayer.beginTime = beginTime;
    //    NSLog(@"beginTime:%@",beginTime);
        
    }
    #pragma mark - 动画代理的方法
    //动画开始
    -(void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"animationDidStart");
    }
    //动画结束
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        //
        NSLog(@"%@",((CABasicAnimation *)anim).toValue);
        NSValue *toValue = ((CABasicAnimation *)anim).toValue;
        CGPoint location = [toValue CGPointValue];
        self.subLayer.position = location;
    }
    
    @end

    六、CAKeyframeAnimation(关键帧动画)

      关键帧动画也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值

      属性说明:

      - values:上述的NSArray对象。里面的元素称为“帧”。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧。

      - path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和positon起作用。如果设置了path,那么values将被忽略

      - keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的。

    #import "ViewController.h"
    #define MAX_TOUCH_NUM 5//最大点击次数
    @interface ViewController ()
    @property(strong,nonatomic)NSMutableArray *points;
    @property(strong,nonatomic)CALayer *subLayer;
    @property(assign,nonatomic)CGMutablePathRef path;
    @property(strong,nonatomic)CALayer *drawLayer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //添加绘图层
        self.drawLayer = [CALayer layer];
        self.drawLayer.bounds = self.view.bounds;
        self.drawLayer.position = self.view.center;
        self.drawLayer.backgroundColor = [[UIColor whiteColor]CGColor];
        [self.view.layer addSublayer:self.drawLayer];
        
        //创建子层
        self.subLayer = [CALayer layer];
        self.subLayer.bounds = CGRectMake(0, 0, 100, 100);
        self.subLayer.position = CGPointMake(100, 100);
        self.subLayer.backgroundColor = [[UIColor greenColor]CGColor];
        
        [self.view.layer addSublayer:self.subLayer];
        
        self.points = [NSMutableArray array];
    //    //设置跟layer的代理
        self.drawLayer.delegate = self;
        
    }
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInView:self.view];
    
        //创建路径
        self.path = CGPathCreateMutable();
        CGPathMoveToPoint(self.path, nil, location.x, location.y);
        
    }
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //将当前点加到路径中去
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInView:self.view];
        CGPathAddLineToPoint(self.path,nil, location.x, location.y);
        [self.drawLayer setNeedsDisplay];
    }
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //创建帧动画(路径动画)
        CAKeyframeAnimation *keyFrame = [[CAKeyframeAnimation alloc]init];
        keyFrame.keyPath = @"position";
        keyFrame.path = self.path;
        keyFrame.duration = 3.0f;
        //设置动画保存在最后的位置
        keyFrame.removedOnCompletion = NO;
        keyFrame.fillMode = kCAFillModeForwards;
        
        keyFrame.delegate = self;
        [self.subLayer addAnimation:keyFrame forKey:@"keyFrame"];
        
    }
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        CGPathRelease(self.path);
    }
    #pragma mark - 层绘制的代理方法
    -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
    {
        CGContextAddPath(ctx, self.path);
        CGContextSetStrokeColorWithColor(ctx, [[UIColor blueColor]CGColor]);
        CGContextDrawPath(ctx, kCGPathStroke);
        
    }
    @end

      运行效果图,如下:  

     七、CAAnimationGroup动画组

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

      属性说明:

      - animations:用来管理多个动画对象的NSArray。默认情况下,多个动画是同时执行的,可以通过设置动画对象的beginTime来改变动画的开始执行时间。

      案例分析:

    #import "ViewController.h"
    
    @interface ViewController ()
    @property(strong,nonatomic)CALayer *subLayer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建子层
        self.subLayer = [CALayer layer];
        self.subLayer.bounds = CGRectMake(0, 0, 100, 100);
        self.subLayer.position = CGPointMake(100, 100);
        self.subLayer.backgroundColor = [[UIColor blueColor]CGColor];
        
        [self.view.layer addSublayer:self.subLayer];
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
        tap.numberOfTapsRequired = 1;
        tap.numberOfTouchesRequired = 1;
        [self.view addGestureRecognizer:tap];
    }
    //创建旋转动画
    -(CABasicAnimation *)rotationAnimationFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue
    {
        //创建基本动画
        CABasicAnimation *animationRotation = [[CABasicAnimation alloc]init];
        animationRotation.keyPath = @"transform.rotation.z";
        animationRotation.fromValue = @(fromValue);
        animationRotation.toValue = @(toValue);
        animationRotation.duration = 1.0f;
        
        animationRotation.removedOnCompletion = NO;
        animationRotation.fillMode = kCAFillModeForwards;
        return animationRotation;
        
    }
    //创建缩放动画
    -(CABasicAnimation *)scaleAnimationFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue
    {
        CABasicAnimation *scaleAnimation = [[CABasicAnimation alloc]init];
        scaleAnimation.keyPath = @"transform.scale";
        scaleAnimation.fromValue = @(fromValue);
        scaleAnimation.toValue = @(toValue);
        scaleAnimation.duration = 2.0f;
        scaleAnimation.removedOnCompletion = NO;
        scaleAnimation.fillMode = kCAFillModeForwards;
        return scaleAnimation;
        
    }
    -(void)tap:(UITapGestureRecognizer *)sender
    {
        //创建动画组
        CAAnimationGroup *group = [[CAAnimationGroup alloc]init];
        NSArray *animations = @[[self rotationAnimationFromValue:-M_PI toValue:-M_PI_4],                        [self scaleAnimationFromValue:1.0 toValue:2.0]];
        group.animations = animations;
        //动画组中的动画的属性,受动画组的控制(比如:动画内设置了大小,时长,在动画组内不起作用,以动画组设置为准)
        group.duration = 4.0f;
    //        group.removedOnCompletion = NO;
    //        group.fillMode = kCAFillModeForwards;
        //延时执行
    //    group.beginTime = CACurrentMediaTime()+1.0f;
        [self.subLayer addAnimation:group forKey:@"animation"];
        
    }
    @end

    八、CATransition转场动画

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

      UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。
      动画属性:
      –type:动画过渡类型
      –subtype:动画过渡方向
      –startProgress:动画起点(在整体动画的百分比)
      –endProgress:动画终点(在整体动画的百分比)
    #import "ViewController.h"
    
    @interface ViewController ()
    @property(strong,nonatomic)UIImageView *imageView;
    @property(assign,nonatomic)NSInteger index;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        self.index = 0;
        
        //创建imageView;
        self.imageView = [[UIImageView alloc]initWithFrame:self.view.frame];
        self.imageView.contentMode = UIViewContentModeScaleAspectFit;
        self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",self.index]];
        self.imageView.userInteractionEnabled = YES;
        [self.view addSubview:self.imageView];
        
        UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
        leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft;
        [self.imageView addGestureRecognizer:leftSwipe];
        
        UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
        rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
        [self.imageView addGestureRecognizer:rightSwipe];
    }
    -(void)leftSwipe:(UISwipeGestureRecognizer *)sender
    {
        if (sender.direction == UISwipeGestureRecognizerDirectionLeft)
        {
            if (self.index > 0)
            {
                self.index--;
            }
        }
        else
        {
            if (self.index < 9)
            {
                self.index++;
            }
        }
        //过场动画
        CATransition *transition = [[CATransition alloc]init];
        //动画类型
        transition.type = @"cube";
        //设置方向
        transition.subtype = kCATransitionFromBottom;
        //设置时长
        transition.duration = 0.5f;
        //添加动画
        [self.imageView.layer addAnimation:transition forKey:@"transition"];
        
        self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",self.index]];
    }
    
    @end

      到此,核心动画就完结了。

  • 相关阅读:
    elasticsearch CriteriaQuery查询例子
    mysql转ElasticSearch的分析 及JAVA API 初探
    java利用HttpClient进行https接口调用
    JDK8新特性:使用stream、Comparator和Method Reference实现集合的优雅排序
    java爬虫入门
    AndroidStudio升到最新版本(3.1.2)之后
    怎样录制简单GIF动图
    android 集成友盟分享之后,想自定义分享面板的看过来
    Android代码中设置字体大小,字体颜色,显示两种颜色.倒计时效果
    Android新特性之CardView的简单使用
  • 原文地址:https://www.cnblogs.com/xjf125/p/4883030.html
Copyright © 2020-2023  润新知