• iOS CoreAnimation详解(一) 有关Layer的动画


    以前由于项目需要 也写了一些动画 ,但是知识不系统,很散。这段时间趁着项目完成的空袭,来跟着大神的脚步系统的总结一下iOS中Core Animation的知识点。

    原博客地址:http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html

    本文主要从CoreAnimation的Layer角度来讲解动画,我想从CALayer的角度更好理解,后续还会有第二篇从UIKIt的UIView角度来讲解动画,第三篇讲解UIDynamicAnimation,第三篇我会讲到UIViewController切换时候的动画。

    本文主要涵盖四个部分

    1.基础动画 会讲到时间函数和一些关键的属性

    2.基于关键帧的动画 讲到沿着指定路径运行的动画

    3.动画组 多个动画组合到一起形成复杂的动画

    4.简单讲一讲有关动画的代理

    一 为什么要设计动画

    动画提供了一个渐变的方式来表达变化,使用动画可以避免各种动画突变,造成用户困惑。

    iOS中,使用CoreAnimation只要指定始末状态或者关键帧状态,CoreAnimation会高效的为我们创建补间动画。

    二 从CALayer的角度来看三种动画

    首先不熟悉CALayer的同学看看前两篇的CALayer的内容,这是CoreAnimation的基础。这里我重复的介绍两种CALayer的Tree。
    Presentation Tree-对应在动画的过程中,CALayer的属性
    Model Tree-对应CALayer的实际属性。
    通过使用 -[CALayer presentationLayer] 和 -[CALayer modelLayer]可以访问两种Tree
    动画的过程实际上是修改Presentation Tree

    2.1 基础的动画 CABasicAnimation

    属性说明

    动画过程说明

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

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

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

    CAKeyframeAnimation——关键帧动画

    关键帧动画,也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:

    CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
    CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation

    属性说明:

    CAAnimationGroup——动画组

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

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

    属性说明:

    第一个简单的动画,我希望imageview向右移动100的距离,移动方式easeInOut(加速开始,减速结束)。

    代码如下,通常有两种方式来影响动画

    CABasicAnimation *animation = [CABasicAnimation animation];
        animation.keyPath = @"position.x";//KVC的方式来访问属性
        animation.fromValue = @(self.imageView.layer.position.x);//该属性开始的值
        animation.toValue = @(self.imageView.layer.position.x + 100);//该属性的结束值
        animation.duration = 1;//完成一次动画需要的时间
        //设置动画进行的方式
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
        [self.imageView.layer addAnimation:animation forKey:@"basic"];

    通过fromValue和toValue是一种方式,当然也可以通过byValue的方式,byValue在初值上加上byValue的变化,通过以下代码也可以实现上述动画

        CABasicAnimation *animation = [CABasicAnimation animation];
        animation.keyPath = @"position.x";//KVC的方式来访问属性
    //    animation.fromValue = @(self.imageView.layer.position.x);//该属性开始的值
    //    animation.toValue = @(self.imageView.layer.position.x + 100);//该属性的结束值
        animation.byValue = @(100);
        animation.duration = 1;//完成一次动画需要的时间
        //设置动画进行的方式
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
        [self.imageView.layer addAnimation:animation forKey:@"basic"];

    但是,结束后会发现,imageview又恢复到原处。这是因为在动画的过程中,我们修改的是Presentation Tree,并没有实际修改CALayer的属性。想要让动画停在结束的位置,通常有两种方式,

    (1)修改属性
    代码如下

    CABasicAnimation * animation = [CABasicAnimation animation];  
      animation.keyPath = @"position.x";  
      animation.fromValue = @(self.imageview.layer.position.x);  
      animation.toValue = @(self.imageview.layer.position.x + 100);  
      animation.duration = 1;  
      animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];  
      [self.imageview.layer addAnimation:animation forKey:@"basic"];  
      self.imageview.layer.position = CGPointMake(self.imageview.layer.position.x+100, self.imageview.layer.position.y);  

    (2)设置让动画停在结束的位置

    CABasicAnimation * animation = [CABasicAnimation animation];  
        animation.keyPath = @"position.x";  
        animation.fromValue = @(self.imageview.layer.position.x);  
        animation.toValue = @(self.imageview.layer.position.x + 100);  
        animation.duration = 1;  
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];  
        animation.removedOnCompletion = NO;//动画结束了禁止删除  
        animation.fillMode = kCAFillModeForwards;//停在动画结束处  
        [self.imageview.layer addAnimation:animation forKey:@"basic"];  

    一般采用前者,因为动画往往的结束是实际的属性的改变。

    这里再讲解下时间函数
    时间函数决定了动画如何执行,时间函数决定了动画的数学模型,比如速度速度最好不要有突变, 系统提供的时间函数有以下几种

    NSString *constkCAMediaTimingFunctionLinear;线性
    NSString *constkCAMediaTimingFunctionEaseIn;加速进入
    NSString *constkCAMediaTimingFunctionEaseOut;减速停止
    NSString*constkCAMediaTimingFunctionEaseInEaseOut;加速进入减速停止,这个是常用的
    NSString *constkCAMediaTimingFunctionDefault;默认
    当然,时间函数支持自定义,用如下函数
    functionWithControlPoints::::
    这个函数的4个点决定了一个三维的贝塞尔曲线来决定时间函数。这里不深入讲解了。
    最后,这一点尤为重要,就是在传递CAAnimation的对象或者子类给Layer的时候,传递的是copy

    2.2 基于关键帧的动画
    以下是一个基于关键帧创建的抖动的动画,采用在时间点对应的位置来创建动画

    CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
        animation.keyPath = @"position.x";
        NSInteger initalPositionX = self.imageView.layer.position.x;
        animation.values = @[@(initalPositionX),
                             @(initalPositionX + 10),
                             @(initalPositionX - 10),
                             @(initalPositionX + 10),
                             @(initalPositionX)];
        animation.keyTimes = @[
                               @(0),
                               @(1/6.0),
                               @(3/6.0),
                               @(5/6.0),
                               @(1)];
        [self.imageView.layer addAnimation:animation forKey:@"keyFrame"];

    当然,基于关键帧的动画支持沿着路径运动,可以设置时间函数来决定运动的方式
    例如以下创建一个比较复杂的运动,首先移动到(200,200),然后沿着以该点为圆心,逆时针旋转半圈,最后停在结束的位置。
    动画的过程中,imageview沿着路径转动

    CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
        animation.keyPath = @"position";
        //Create Path
        CGMutablePathRef mutablepath = CGPathCreateMutable();
        CGPathMoveToPoint(mutablepath, nil,self.imageView.layer.position.x, self.imageView.layer.position.y);
        CGPathAddLineToPoint(mutablepath,nil,200,200);
        CGPathAddArc(mutablepath, nil,200,200,100,0,M_PI,YES);
        //set path
        animation.path = mutablepath;
        animation.duration = 4.0;
        animation.rotationMode = kCAAnimationRotateAuto;
        animation.removedOnCompletion = NO;//动画结束了禁止删除
        animation.fillMode = kCAFillModeForwards;//停在动画结束处
        [self.imageView.layer addAnimation:animation forKey:@"PathAnimation"];

    2.3 动画组
    所谓,动画组就是把几个动画组合到一起,然后一起执行,通常复杂的动画都是由动画组来实现的。
    举个例子:沿着上个例子的路径运动,运动的同时透明度渐变。

    CAKeyframeAnimation * pathAnimation = [CAKeyframeAnimation animation];  
      pathAnimation.keyPath = @"position";  
      //Create Path  
      CGMutablePathRef mutablepath = CGPathCreateMutable();  
      CGPathMoveToPoint(mutablepath, nil,self.imageview.layer.position.x, self.imageview.layer.position.y);  
      CGPathAddLineToPoint(mutablepath,nil,200,200);  
      CGPathAddArc(mutablepath, nil,200,200,100,0,M_PI,YES);  
      //set path  
      pathAnimation.path = mutablepath;  
      pathAnimation.rotationMode = kCAAnimationRotateAuto;  
      [self.imageview.layer addAnimation:pathAnimation forKey:@"PathAnimation"];  
        
      //透明度变化  
      CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animation];  
      opacityAnimation.keyPath = @"opacity";  
      opacityAnimation.values = @[@(1.0),  
                           @(0.5),  
                           @(0.0),  
                           @(0.5),  
                           @(1.0)];  
      opacityAnimation.calculationMode = kCAAnimationPaced;  
      [self.imageview.layer addAnimation:opacityAnimation forKey:@"OpacityAnination"];  
      //配置动画组  
      CAAnimationGroup * animationGroup = [[CAAnimationGroup alloc] init];  
      animationGroup.animations = @[pathAnimation,opacityAnimation];  
      animationGroup.duration = 4.0;  
      animationGroup.removedOnCompletion = NO;  
      animationGroup.fillMode = kCAFillModeBackwards;  
      [self.imageview.layer addAnimation:animationGroup forKey:@"GroupAnimation"];  

    (三)动画的代理
    通过设置代理可以监听动画开始和结束的事件
    通过设置delegate,来监听两个函数
    animationDidStart:(CAAnimation *)anim
    animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    这里的flag判断动画是否执行完毕

    4.付coreAnimation中的keyPath 属性

    CAAnimation——简介

    是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类。

    基本属性说明

    fillMode 属性设置

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

    kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态

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

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

    CALayer上动画的暂停和恢复

    #pragma mark 暂停CALayer的动画
    -(void)pauseLayer:(CALayer*)layer
    {
        CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    
        // 让CALayer的时间停止走动
          layer.speed = 0.0;
        // 让CALayer的时间停留在pausedTime这个时刻
        layer.timeOffset = pausedTime;
    }
    
    #pragma mark 恢复CALayer的动画
    -(void)resumeLayer:(CALayer*)layer
    {
        CFTimeInterval pausedTime = layer.timeOffset;
        // 1. 让CALayer的时间继续行走
          layer.speed = 1.0;
        // 2. 取消上次记录的停留时刻
          layer.timeOffset = 0.0;
        // 3. 取消上次设置的时间
          layer.beginTime = 0.0;
        // 4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
        CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
        // 5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
          layer.beginTime = timeSincePause;
    }
  • 相关阅读:
    为什么要使用Handler
    使用Java中的Timer和TimerTask
    Top子句对查询计划的影响
    一个单表死锁的示例
    tracer token 追踪标记
    DDL Trigger
    事物复制的troubleshooting 1
    在分发服务器上查看信息
    将windows 2003 sp2的cluster升级到windows 2008 r2
    DistributionDB过大的原因
  • 原文地址:https://www.cnblogs.com/huanying2000/p/6245104.html
Copyright © 2020-2023  润新知