QuartzCore之CoreAnimation核心动画介绍

1. 简介

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

2. 核心动画类的层次结构

核心动画位于CAAnimation.h中,该文件提供了以下接口及类层级结构


CAAnimation类继承关系.jpeg

如上图所示,CoreAnimation所使用类如下:

  • CAMediaTiming:CAAnimation实现的协议,共有8个属性控制动画的过程;

  • CAAnimation:该类是所有动画的抽象类,不能直接使用,只能使用其子类。并且实现了CAMediaTiming协议;

  • CAPropertyAnimation:是CAAnimation的子类,支持动画

  • CABasicAnimation:基本动画-

  • CATransition:转场动画

  • CAKeyframeAnimation:关键帧动画

  • CAAnimationGroup:组动画

  • CASpringAnimation:弹簧动画

3. CAMediaTiming协议

CAMediaTiming协议共有8个属性,每个属性简单介绍如下

属性 说明
duration 动画持续时间
beginTime 动画相对一个对象开始的时间,起到延时执行的目的
speed 动画执行速度
timeOffset 动画执行偏移
repeatCount 动画执行重复次数
repeatDuration 动画执行时间
autoreverses 动画结束后是否反向执行
fillMode 决定当前对象在非active时间段的行为

3.1. duration

代码如下:

#import "ViewController.h"

#define kScreenW ([UIScreen mainScreen].bounds.size.width)
#define kScreenH ([UIScreen mainScreen].bounds.size.height)

@interface ViewController ()

@property (nonatomic,weak) CALayer *redLayer;
@property (nonatomic,weak) CALayer *blueLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
 
    //1  添加一个红色的Layer
    CALayer *redLayer = [CALayer layer];
    self.redLayer = redLayer;
    redLayer.backgroundColor = [UIColor redColor].CGColor;
    redLayer.frame = CGRectMake(0, 0, kScreenW/2.0, 50);
    [self.view.layer addSublayer:redLayer];
    
    //1  添加一个蓝色的Layer
    CALayer *blueLayer =[CALayer layer];
    self.blueLayer = blueLayer;
    blueLayer.backgroundColor =[UIColor blueColor].CGColor;
    blueLayer.frame = CGRectMake(kScreenW/2.0, 0, kScreenW/2.0, 50);
    [self.view.layer addSublayer:blueLayer];
    
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  
    //给红色Layer添加动画
    CABasicAnimation *redLayerBasicAnimation = [CABasicAnimation animation];
    redLayerBasicAnimation.keyPath=@"position";
    redLayerBasicAnimation.fromValue=[NSValue valueWithCGPoint:self.redLayer.position];
    redLayerBasicAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake(self.redLayer.position.x, kScreenH)];
    
    //设置动画持续时间
    redLayerBasicAnimation.duration = 10;

    [self.redLayer addAnimation:redLayerBasicAnimation forKey:nil];
    
    
    //给蓝色Layer添加动画
    CABasicAnimation *blueLayerBasicAnimation = [CABasicAnimation animation];
    blueLayerBasicAnimation.keyPath=@"position";
    blueLayerBasicAnimation.fromValue=[NSValue valueWithCGPoint:self.blueLayer.position];
    blueLayerBasicAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake(self.blueLayer.position.x, kScreenH)];
   
    //设置动画持续时间
    blueLayerBasicAnimation.duration = 5;

    [self.blueLayer addAnimation:blueLayerBasicAnimation forKey:nil];
}
@end

上述代码在视图上添加两个动画,其中一个动画持续10s,另一个动画持续5秒,竖向位移为屏幕高
执行效果如下


动画持续时间左10右5.gif

3.2. beginTime

beginTime为相对时间系开始的时间,可以认为是起到延时左右,设置右侧蓝色Layer相对当前时间系延时5s时间执行

    //相对于当前时间系延后5秒,起到延时作用
    blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;

执行效果显示蓝色相对红色延时5秒后执行,且同时到达底


延时5秒执行.gif

3.3. speed

speed为相对一个时间系,本Layer时间流逝速度,默认情况为1,设置蓝色Layer时间流逝速度为0.5倍则在不设置时间偏移的情况下两者同时到达低端,效果如下:

蓝色Layer0.5倍流逝速度.gif

3.4. timeOffset

该属性主要标记一个动画从何处执行,该属性不存在延迟作用。例如一个动画正常执行如下顺序:

  A   -->  B  -->  C  -->  D   -->  A

此时如果设置 timeOffset 为B状态,则该动画开始执行状态为

  B  -->  C  -->  D   -->  A  -->  B  

设置红色动画持续时间10s,蓝色动画持续时间5秒,设置蓝色动画的timeOffset为2.5秒

  //相对于当前时间系延后5秒,起到延时作用
    //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;
    //设置时间流逝速度
    //blueLayerBasicAnimation.speed = 0.5;
    //设置动画偏移时间
    blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5;

运行效果如下:


设置动画偏移时间.gif

3.5. repeatCount

repeatCount该属性主要记录动画重复持续次数,设定红色Layer动画持续时间为10s,蓝色为5s,新添加蓝色Layer重复次数为2次,代码如下:

blueLayerBasicAnimation.duration = 5;
    //相对于当前时间系延后5秒,起到延时作用
    //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;
    //设置时间流逝速度
    //blueLayerBasicAnimation.speed = 0.5;
    //设置动画偏移时间
    //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5;
    //设置动画重复次数
    blueLayerBasicAnimation.repeatCount = 2;

效果如下:


蓝色重复次数为2次.gif

3.6. repeatDuration

repeatDuration该属性主要记录动画重复持续时间,设定红色Layer动画持续时间为10s,蓝色为5s,新添加蓝色Layer动画持续时间12.5秒,代码如下:

     blueLayerBasicAnimation.duration = 5;
    //相对于当前时间系延后5秒,起到延时作用
    //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;
    //设置时间流逝速度
    //blueLayerBasicAnimation.speed = 0.5;
    //设置动画偏移时间
    //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5;
    //设置动画重复次数
    //blueLayerBasicAnimation.repeatCount = 2;
    //设置动画持续时间
    blueLayerBasicAnimation.repeatDuration = 12.5;

运行效果如下:


蓝色持续12.5秒.gif

3.7. autoreverses

该属性主要执行结束动画后是否执行逆行动画过程,默认NO不执行,如果YES则执行,

   blueLayerBasicAnimation.duration = 5;
    //相对于当前时间系延后5秒,起到延时作用
    //blueLayerBasicAnimation.beginTime = CACurrentMediaTime()+5;
    //设置时间流逝速度
    //blueLayerBasicAnimation.speed = 0.5;
    //设置动画偏移时间
    //blueLayerBasicAnimation.timeOffset =CACurrentMediaTime()+2.5;
    //设置动画重复次数
    //blueLayerBasicAnimation.repeatCount = 2;
    //设置动画持续时间
    //blueLayerBasicAnimation.repeatDuration = 12.5;
    //动画
     blueLayerBasicAnimation.autoreverses = YES;

是否执行逆行动画.gif

3.8. fillMode

fillMode决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后 ,但是该属性要和CAAnimation中的removedOnCompletion属性配合着使用.

/* When true, the animation is removed from the render tree once its
 * active duration has passed. Defaults to YES. */
如果是true,一旦动画的活动期间过了,那么这个动画将被移除,默认值YES
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;

其中fillMode属性值(要想fillMode有效,最好设置removedOnCompletion=NO)存在四中状态,如下:

/* `fillMode' options. */

CA_EXTERN NSString * const kCAFillModeForwards
    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAFillModeBackwards
    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAFillModeBoth
    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAFillModeRemoved
    __OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
  • ** kCAFillModeForwards** :当动画结束后,layer会保持在动画结束状态.
  • ** kCAFillModeBackwards** :当动画结束后,layer会一直保持着动画最后的状态.
  • ** kCAFillModeBoth** :这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状
  • ** kCAFillModeRemoved** :这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态

测试kCAFillModeForwards状态代码如下:

    blueLayerBasicAnimation.fillMode = kCAFillModeForwards;
    blueLayerBasicAnimation.removedOnCompletion = NO;

运行效果如下:


kCAFillModeForwards状态测试.gif

4.0 CAAnimation抽象类

CAAnimation是Core Animation所有动画类型的抽象基类。作为一个抽象类,CAAnimation本身并没有做多少工作,它提供了一个计时函数(用于动画的进入进出状态),一个委托(用于反馈动画状态)以及一个removedOnCompletion,用于标识动画是否该在结束后自动释放(默认YES,为了防止内存泄露)。CAAnimation同时实现了一些协议,包括CAMediaTiming。

/* Creates a new animation object. */

+ (instancetype)animation;

/* Animations implement the same property model as defined by CALayer.
 * See CALayer.h for more details. */

+ (nullable id)defaultValueForKey:(NSString *)key;
- (BOOL)shouldArchiveValueForKey:(NSString *)key;

/* A timing function defining the pacing of the animation. Defaults to
 * nil indicating linear pacing. */

@property(nullable, strong) CAMediaTimingFunction *timingFunction;

/* The delegate of the animation. This object is retained for the
 * lifetime of the animation object. Defaults to nil. See below for the
 * supported delegate methods. */

@property(nullable, strong) id delegate;

/* When true, the animation is removed from the render tree once its
 * active duration has passed. Defaults to YES. */

@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;

4.1. +animation方法

该方法为抽象方法,由子类创建实例对象,生成相应的对象类型。例如基本动画、关键帧动画、组动画、转场动画。

4.2. timingFunction属性

摘录参考blog
Timing Function的会被用于变化起点和终点之间的插值计算.形象点说是Timing Function决定了动画运行的节奏(Pacing),比如是均匀变化(线性变化)、先快后慢、先慢后快、还是先慢再快再慢.

时间函数是使用的一段函数来描述的,横座标是时间t取值范围是0.0-1.0,纵座标是变化量x(t)也是取值范围也是0.0-1.0 假设有一个动画,duration是8秒,变化值的起点是a终点是b(假设是透明度),那么在4秒处的值是多少呢? 可以通过计算为 a + x(4/8) * (b-a), 为什么这么计算呢?讲实现的时间映射到单位值的时候4秒相对于总时间8秒就是0.5然后可以得到0.5的时候单位变化量是 x(0.5), x(0.5)/1 = 实际变化量/(b-a), 其中b-a为总变化量,所以实际变化量就是x(0.5) * (b-a) ,最后4秒时的值就是 a + x(0.5) * (b-a),所以计算的本质是映射

Timing Function对应的类是CAMediaTimingFunction,它提供了两种获得时间函数的方式,一种是使用预定义的五种时间函数,一种是通过给点两个控制点得到一个时间函数. 相关的方法为

+ (instancetype)functionWithName:(NSString *)name;

+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

- (void)getControlPointAtIndex:(size_t)idx values:(float[2])ptr;

五种预定义的时间函数名字的常量变量分别为

  • ** ** kCAMediaTimingFunctionLinear,
  • ** **kCAMediaTimingFunctionEaseIn,
  • ** **kCAMediaTimingFunctionEaseOut,
  • ** **kCAMediaTimingFunctionEaseInEaseOut,
  • ** **kCAMediaTimingFunctionDefault.

下图展示了前面四种Timing Function的曲线图,横座标表示时间,纵座标表示变化量,这点需要搞清楚(并不是平面座标系中xy).

时间曲线.jpg

自定义的Timing Function的函数图像就是一条三次贝塞尔曲线Cubic Bezier Curve,贝塞尔曲线的优点就是光滑,用在这里就使得变化显得光滑.一条三次贝塞尔曲线可以由起点终点以及两个控制点决定.
上面的kCAMediaTimingFunctionDefault对应的函数曲线其实就是通过[(0.0,0.0), (0.25,0.1), (0.25,0.1), (1.0,1.0)]这四个点决定的三次贝塞尔曲线,头尾为起点和终点,中间的两个点是控制点.

贝塞尔曲线.jpg

上图中P0是起点,P3是终点,P1和P2是两个控制点

4.3. delegate代理属性

CAAnimation提供代理协议,代理方法提供了动画开始和动画结束方法,通过代理方法执行回调。

@interface NSObject (CAAnimationDelegate)

/* Called when the animation begins its active duration. */

- (void)animationDidStart:(CAAnimation *)anim;

/* Called when the animation either completes its active duration or
 * is removed from the object it is attached to (i.e. the layer). 'flag'
 * is true if the animation reached the end of its active duration
 * without being removed. */

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

@end

4.4. removedOnCompletion

该方法前边已经介绍,用于移除动画使用

5.0. CAPropertyAnimation

CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类才有,主要存在以下方法和属性

@interface CAPropertyAnimation : CAAnimation

+ (instancetype)animationWithKeyPath:(nullable NSString *)path;

@property(nullable, copy) NSString *keyPath;
@property(getter=isAdditive) BOOL additive;
@property(getter=isCumulative) BOOL cumulative;
@property(nullable, strong) CAValueFunction *valueFunction;

@end

6.0 CABasicAnimation基础动画

CABasicAnimation 基础动画其实就是一段时间内发生的改变,最简单的形式就是从一个值改变到另一个值,

@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;

下面这段英文摘自苹果官方文档,将的是fromValue toValue ByValue 怎么使用
The interpolation values are used as follows:

  • ** **Both fromValue and toValue are non-nil. Interpolates between fromValue and toValue.
    翻译:fromValue和toValue都不为空,在fromValue到toValue之间插值

  • ** **fromValue and byValue are non-nil. Interpolates between fromValue and (fromValue + byValue).
    翻译:fromValue和byValue都不为空,在fromValue到fromValue +byValue之间插值

  • ** **byValue and toValue are non-nil. Interpolates between (toValue - byValue) and toValue.
    翻译:byValue和toValue都不为空,在 (toValue - byValue) 到toValue之间插值

  • ** **fromValue is non-nil. Interpolates between fromValue and the current presentation value of the property.
    翻译:fromValue不为空,在 fromValue到对象当前值之间插值

  • ** **toValue is non-nil. Interpolates between the current value of keyPath in the target layer’s presentation layer andtoValue.
    翻译:toValue不为空,在 对象当前值到toValue之间插值

  • ** **byValue is non-nil. Interpolates between the current value of keyPath in the target layer’s presentation layer and that value plus byValue.
    翻译:byValue不为空,在 对象当前值到经过byValue值之间插值

  • ** **All properties are nil. Interpolates between
    the previous value of keyPath in the target layer’s presentation layer and
    the current value of keyPath in the target layer’s presentation layer.
    翻译:都不为空,在 对象以前值到对象当前值之间插值(不懂)

7. CAKeyframeAnimation关键帧动画

CAKeyframeAnimation 关键帧动画,该动画从字面简单的理解为做动画时只需要给出动画部分关键帧即可,系统会通过插值方式自己完成动画,在文件中存在以属性:

/** General keyframe animation class. **/
@interface CAKeyframeAnimation : CAPropertyAnimation

@property(nullable, copy) NSArray *values;
@property(nullable) CGPathRef path;
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
@property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions;
@property(copy) NSString *calculationMode;
@property(nullable, copy) NSArray<NSNumber *> *tensionValues;
@property(nullable, copy) NSArray<NSNumber *> *continuityValues;
@property(nullable, copy) NSArray<NSNumber *> *biasValues;
@property(nullable, copy) NSString *rotationMode;

@end

7.1 values属性

values属性为NSArray类型,里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
代码如下:

   CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position.y";
    animation.values = @[@150,@400,@100,@400];
    animation.duration = 5.f;
  
    [self.testLayer addAnimation:animation forKey:nil];

执行动画效果:

关键帧动画.gif

7.2 keyTimes属性

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

keyTimes值要和运行时间段吻合,如果不吻合会出现以下问题:
第一种情况:吻合情况

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position.y";
    animation.values = @[@400,@0,@400,@0];//定义了4帧
    animation.duration = 5.f;
    
    //设置每一个关键帧的时间
    animation.keyTimes = @[@0,@(1/8.),@(3/8.),@1];
    
    [self.testLayer addAnimation:animation forKey:nil];

效果如下:

关键帧动画添加keyTimes属性.gif

分析:
添加keyTimes记录着执行到每一帧动画的时间点,例如:
@400 @0 为起始值
@400 - > @0过程中,必须在@(1/8.)执行到@0位置
@0 - >@400过程中,必须在@(3/8.)执行到@ 400位置
@400 - > @0过程中,必须在@1执行到@0位置
第二种情况:少给值了,少给则提前结束

 //设置每一个关键帧的时间
    animation.keyTimes = @[@0,@(1/8.),@1];

运行效果:

keyTime值数量过少.gif

分析:
添加keyTimes记录着执行到每一帧动画的时间点,例如:
@400 @0 为起始值
@400 - > @0过程中,必须在@(1/8.)执行到@0位置
@0 - >@400过程中,必须在@(1)执行到@ 400位置,由于@1时间结束,所以动画直接结束

第三种情况:多给值了,则按照顺序执行,剩余时间无效

CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position.y";
    animation.values = @[@400,@0,@400,@0];//定义了4帧
    animation.duration = 5.f;
    
    //设置每一个关键帧的时间
    animation.keyTimes = @[@0,@(1/8.),@(2/8.),@(3/8.),@(4/8.),@1];
    
    [self.testLayer addAnimation:animation forKey:nil];

效果:


keyTimes多给情况.gif

分析:
添加keyTimes记录着执行到每一帧动画的时间点,例如:
@400 @0 为起始值
@400 - > @0过程中,必须在@(1/8.)执行到@0位置
@0 - >@400过程中,必须在@(2/8.)执行到@ 400位置
@400 - > @0过程中,必须在@@(3/8.)执行到@0位置
剩余时间@(3/8.) -> @1这个时间段属于无效冗余时间,所以动画在等待到@1这一时刻立即结束。

7.3. timingFunctions

该属性在上述已经介绍过了,只不过是在动画每一个执行路径过程中执行的时间类型函数,如果你values个数为N,则timingFunctions 个数至少为N-1;否则剩余部分则执行线性时间函数。

代码例子:

    animation.timingFunctions = @[
                                         [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                         [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                         [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                         [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];

运行效果:

timingFunctions时间函数.gif

7.4. calculationMode属性

在关键帧动画中还有一个非常重要的参数,那便是calculationMode,计算模式.其主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint 和 position 进行的动画.当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算. calculationMode目前提供如下几种模式 kCAAnimationLinear

  • ** kCAAnimationLinear :** calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;
  • ** kCAAnimationDiscrete ** 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示;
  • ** kCAAnimationPaced ** 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效;
  • ** kCAAnimationCubic ** 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,这里的数学原理是Kochanek–Bartels spline,这里的主要目的是使得运行的轨迹变得圆滑;
  • ** kCAAnimationCubicPaced ** 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的.

对于这五种效果分别如下

1.gif

8. CAAnimationGroup组动画

组动画,比较简单为两个动画的结合体

@interface CAAnimationGroup : CAAnimation

@property(nullable, copy) NSArray<CAAnimation *> *animations;

@end

代码如下:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,weak) CALayer *myLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
 
    CALayer *myLayer=[[CALayer alloc] init];
    self.myLayer=myLayer;
 
    myLayer.bounds=CGRectMake(0, 0, 100, 30);

    myLayer.position=CGPointMake(200, 200);

    myLayer.backgroundColor=[UIColor redColor].CGColor;
    
    [self.view.layer addSublayer:myLayer];

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    

    //第一组动画
    CAKeyframeAnimation  *keyframe=[CAKeyframeAnimation animationWithKeyPath:nil];
 
    keyframe.keyPath=@"position";
    
    UIBezierPath *path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(200 , 200) radius:150 startAngle:0 endAngle:2*M_PI  clockwise:YES];
    keyframe.path=path.CGPath;
    
    //第二组
    CABasicAnimation *basic=[CABasicAnimation animationWithKeyPath:nil];

    basic.keyPath=@"transform.rotation";
    basic.byValue=@(2*M_PI *20);
    
    //把两组动画组成组动画
    CAAnimationGroup *group=[[CAAnimationGroup alloc] init];

    group.duration=10;
    group.repeatCount=INT16_MAX;
    group.animations=@[keyframe,basic];
    
    [self.myLayer addAnimation:group forKey:nil];
    
    
}

@end

运行效果:

组动画.gif

9. CATransition转场动画


/** Transition animation subclass. **/

@interface CATransition : CAAnimation

@property(copy) NSString *type;
@property(nullable, copy) NSString *subtype;
@property float startProgress;
@property float endProgress;
@property(nullable, strong) id filter;

@end

9.1 type动画过渡类型

主要提供了4中过渡方式

  • ** kCATransitionFade**交叉淡化过渡(不支持过渡方向)
  • ** kCATransitionMoveIn**新视图移到旧视图上面
  • ** kCATransitionPush**新视图把旧视图推出去
  • ** kCATransitionReveal**将旧视图移开,显示下面的新视图

私有API

  • cube //立方体翻滚效果
  • oglFlip //上下左右翻转效果
  • suckEffect //收缩效果,如一块布被抽走(不支持过渡方向)
  • **rippleEffect **//滴水效果(不支持过渡方向)
  • **pageCurl ** //向上翻页效果
  • pageUnCurl //向下翻页效果
  • **cameraIrisHollowOpen ** //相机镜头打开效果(不支持过渡方向)
  • cameraIrisHollowClose //相机镜头关上效果(不支持过渡方向)

下面看下suckEffect效果:

suckEffect.gif

9.2 subtype动画过渡方向

规定了动画从哪个方面过来

  • ** kCATransitionFromRightl**
  • ** kCATransitionFromLeftl**
  • ** kCATransitionFromTopl**
  • ** kCATransitionFromBottoml**

9.2 startProgress和endProgress动画起点/终点百分比

 transition.startProgress = 0.5;

效果:

startProgress = 0.5.gif

由此可以看出:动画初始时刻已经执行了0.5,所以我们看到的是执行剩余部分的0.5部分。

transition.endProgress = 0.8;

效果:

endProgress = 0.8.gif

由此可以看出:动画在执行到0.8进度的时候,突然结束

10. CASpringAnimation弹簧动画


@interface CASpringAnimation : CABasicAnimation

@property CGFloat mass;//目标质量
@property CGFloat stiffness;//劲度系数
@property CGFloat damping;//阻尼系数
@property CGFloat initialVelocity;//初始速度
@property(readonly) CFTimeInterval settlingDuration;//结算时间,结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
通常弹簧动画的时间使用结算时间比较准确

@end

代码示例:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) CALayer *testLayer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    CALayer *testLayer = [CALayer new];
    self.testLayer = testLayer;
    
    [self.view.layer addSublayer:testLayer];
    
    testLayer.backgroundColor = [UIColor redColor].CGColor;
    testLayer.frame = CGRectMake(150, 100, 100, 100);
    
    UIView *lineView1 = [[UIView alloc ] initWithFrame:CGRectMake(0, self.testLayer.position.y+200.0, [UIScreen mainScreen].bounds.size.width, 1)];
    [self.view addSubview:lineView1];
    lineView1.backgroundColor = [UIColor blueColor];
    
    UIView *lineView2 = [[UIView alloc ] initWithFrame:CGRectMake(0, self.testLayer.position.y, [UIScreen mainScreen].bounds.size.width, 1)];
    [self.view addSubview:lineView2];
    lineView2.backgroundColor = [UIColor blackColor];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
   
    CASpringAnimation *spring  =[CASpringAnimation animation];
    spring.keyPath = @"position.y";
    spring.fromValue = @(self.testLayer.position.y);
    spring.toValue = @(self.testLayer.position.y+200.0);
    
    spring.mass = 5;//质量
    spring.damping = 30;//阻尼系数
    spring.stiffness = 300;//劲度系数
    spring.initialVelocity = 0;//初始化速度
    spring.duration = spring.settlingDuration;//动画执行时间
   
    NSLog(@"settlingDuration=%lf",spring.settlingDuration);
    [self.testLayer addAnimation:spring forKey:nil];
}

@end

运行效果:

CASpringAnimation弹簧动画.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容