CABasicAnimation使用总结

作者: 懒得起名的伊凡 | 来源:发表于2015-10-27 13:43 被阅读34353次

    实例化

    使用方法animationWithKeyPath:对 CABasicAnimation进行实例化,并指定Layer的属性作为关键路径进行注册。

    //围绕y轴旋转
    CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    

    设定动画

    设定动画的属性和说明

    属性 说明
    duration 动画的时长
    repeatCount 重复的次数。不停重复设置为 HUGE_VALF
    repeatDuration 设置动画的时间。在该时间内动画一直执行,不计次数。
    beginTime 指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的方式
    timingFunction 设置动画的速度变化
    autoreverses 动画结束时是否执行逆动画
    fromValue 所改变属性的起始值
    toValue 所改变属性的结束时的值
    byValue 所改变属性相同起始值的改变量
    transformAnima.fromValue = @(M_PI_2);
    transformAnima.toValue = @(M_PI);
    transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transformAnima.autoreverses = YES;
    transformAnima.repeatCount = HUGE_VALF;
    transformAnima.beginTime = CACurrentMediaTime() + 2;
    

    防止动画结束后回到初始状态

    只需设置removedOnCompletionfillMode两个属性就可以了。

    transformAnima.removedOnCompletion = NO;
    transformAnima.fillMode = kCAFillModeForwards;
    

    解释:为什么动画结束后返回原状态?
    首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。

    这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。

    所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。

    添加动画

    [self.imageView.layer addAnimation:transformAnima forKey:@"A"];
    

    需要注意的两点

    • 一个 CABasicAniamtion 的实例对象只是一个数据模型,和他绑定到哪一个layer上是没有关系的
    • 方法addAnimation:forKey:是将 CABasicAniamtion 对象进行了 copy 操作的。所以在将其添加到一个layer上之后,我们还是将其再次添加到另一个layer上的。

    fillMode属性的理解

    该属性定义了你的动画在开始和结束时的动作。默认值是 kCAFillModeRemoved

    取值的解释

    • kCAFillModeRemoved 设置为该值,动画将在设置的 beginTime 开始执行(如没有设置beginTime属性,则动画立即执行),动画执行完成后将会layer的改变恢复原状。
    • kCAFillModeForwards 设置为该值,动画即使之后layer的状态将保持在动画的最后一帧,而removedOnCompletion的默认属性值是 YES,所以为了使动画结束之后layer保持结束状态,应将removedOnCompletion设置为NO。
    • kCAFillModeBackwards 设置为该值,将会立即执行动画的第一帧,不论是否设置了 beginTime属性。观察发现,设置该值,刚开始视图不见,还不知道应用在哪里。
    • kCAFillModeBoth 该值是 kCAFillModeForwards 和 kCAFillModeBackwards的组合状态

    Animation Easing的使用

    也即是属性timingFunction值的设定,有种方式来获取属性值

    (1)使用方法functionWithName:

    这种方式很简单,这里只是简单说明一下取值的含义:

    • kCAMediaTimingFunctionLinear 传这个值,在整个动画时间内动画都是以一个相同的速度来改变。也就是匀速运动。

    • kCAMediaTimingFunctionEaseIn 使用该值,动画开始时会较慢,之后动画会加速。


    • kCAMediaTimingFunctionEaseOut 使用该值,动画在开始时会较快,之后动画速度减慢。


    • kCAMediaTimingFunctionEaseInEaseOut 使用该值,动画在开始和结束时速度较慢,中间时间段内速度较快。


    (2)使用方法functionWithControlPoints: : : :实现,这个之后再说,占个坑先。

    其他的一些设置属性

    • repeatCount 设置动画的执行次数
    • autoreverses 默认值为 NO,将其设置为 YES
    • speed 改变动画的速度 可以直接设置动画上的speed属性,这样只有这个动画速度。
      animation.speed = 2;
      或者在layer上设置speed属性,这样在该视图上的所有动画都提速,该视图上的所有子视图上的动画也会提速。
      speed两点需注意的:
      (1) 如果设置动画时间为4s,speed设置为2,则动画只需2s即可执行完。
      (2)如果同时设置了动画的speed和layer 的speed,则实际的speed为两者相乘。

    使用总结

    • 在动画执行完成之后,最好还是将动画移除掉。也就是尽量不要设置removedOnCompletion属性为NO
    • fillMode尽量取默认值就好了,不要去设置它的值。只有在极个别的情况下我们会修改它的值,以后会说到,这里先占个坑。
    • 解决有时视图会闪动一下的问题,我们可以将layer的属性值设置为我们的动画最后要达到的值,然后再给我们的视图添加layer动画。

    例子(移动动画实现)

    直接上代码

    CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"];
    positionAnima.duration = 0.8;
    positionAnima.fromValue = @(self.imageView.center.y);
    positionAnima.toValue = @(self.imageView.center.y-30);
    positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    positionAnima.repeatCount = HUGE_VALF;
    positionAnima.repeatDuration = 2;
    positionAnima.removedOnCompletion = NO;
    positionAnima.fillMode = kCAFillModeForwards;
    
    [self.imageView.layer addAnimation:positionAnima forKey:@"AnimationMoveY"];
    

    组合动画的实现

    CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"];
    positionAnima.fromValue = @(self.imageView.center.y);
    positionAnima.toValue = @(self.imageView.center.y-30);
    positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    
    
    CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    transformAnima.fromValue = @(0);
    transformAnima.toValue = @(M_PI);
    transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    CAAnimationGroup *animaGroup = [CAAnimationGroup animation];
    animaGroup.duration = 2.0f;
    animaGroup.fillMode = kCAFillModeForwards;
    animaGroup.removedOnCompletion = NO;
    animaGroup.animations = @[positionAnima,transformAnima];
    
    [self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
    

    动画开始和结束时的事件

    为了获取动画的开始和结束事件,需要实现协议

    positionAnima.delegate = self;
    

    代理方法实现

    //动画开始时
    - (void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"开始了");
    }
    
    //动画结束时
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        //方法中的flag参数表明了动画是自然结束还是被打断,比如调用了removeAnimationForKey:方法或removeAnimationForKey方法,flag为NO,如果是正常结束,flag为YES。
        NSLog(@"结束了");
    }
    

    其实比较重要的是有多个动画的时候如何在代理方法中区分不同的动画
    两种方式

    方式一:

    如果我们添加动画的视图是全局变量,可使用该方法。
    添加动画时,我们使用了

    [self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
    

    所以,可根据key来区分不同的动画

    //动画开始时
    - (void)animationDidStart:(CAAnimation *)anim
    {
        if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) {
            NSLog(@"动画组执行了");
        }
    }
    

    Note:把动画存储为一个属性然后再回调中比较,用来判定是哪个动画是不可行的。应为委托传入的动画参数是原始值的一个深拷贝,不是同一个值

    方式二

    添加动画的视图是局部变量时,可使用该方法
    添加动画给动画设置key-value对

    [positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"];
    
    [transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];
    

    所以,可以根据key中不同的值来进行区分不同的动画

    //动画结束时
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) {
            NSLog(@"位置移动动画执行结束");
        }
        else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){
            NSLog(@"旋转动画执行结束");
        }
    }
    

    解决循环引用问题

    由于CAAnimation的delegate使用的strong类型,


    所以在全局变量如下设置时会产生循环引用的情况

    self.animation.delegate = self;//可通过复用dealloc方法来验证
    

    解决方案

    • 声明一个单独的类实现delegate的回调
    //.h
    #import <UIKit/UIKit.h>
    
    @interface AnimationDelegate : NSObject
    
    @end
    
    //.m
    #import "AnimationDelegate.h"
    
    @implementation AnimationDelegate
    
    - (void)animationDidStart:(CAAnimation *)anim
    {
        NSLog(@"Animation Start");
    }
    
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        NSLog(@"Animation Stop");
    }
    
    - (void)dealloc
    {
        NSLog(@"Delegate Dealloc");
    }
    
    @end
    

    使用方式

    self.animation.delegate = [[AnimationDelegate alloc]init];
    
    • 使用NSProxy来解决
      该类可直接引用YYKit中的YYWeakProxy
      使用方法
    self.animation.delegate = [YYWeakProxy proxyWithTarget:self];
    

    一些常用的animationWithKeyPath值的总结

    说明 使用形式
    transform.scale 比例转化 @(0.8)
    transform.scale.x 宽的比例 @(0.8)
    transform.scale.y 高的比例 @(0.8)
    transform.rotation.x 围绕x轴旋转 @(M_PI)
    transform.rotation.y 围绕y轴旋转 @(M_PI)
    transform.rotation.z 围绕z轴旋转 @(M_PI)
    cornerRadius 圆角的设置 @(50)
    backgroundColor 背景颜色的变化 (id)[UIColor purpleColor].CGColor
    bounds 大小,中心不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
    position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(300, 300)];
    contents 内容,比如UIImageView的图片 imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage;
    opacity 透明度 @(0.7)
    contentsRect.size.width 横向拉伸缩放 @(0.4)最好是0~1之间的

    相关文章

      网友评论

      • yadong:大哥 你这个破除循环引用的方法没作用啊
        懒得起名的伊凡:哪一个?可以详细说下?
      • CaptainMi:循环引用怎么解决啊???
      • a0b6e2a49203:请问如何解决CABasicAnimation在切换页面或者home回来之后停止了?看网上animation.removedOnCompletion = NO解决方案对我好像还不行,我的是几个ViewController的View滑动切换。谢谢!
        EE_L:momAnimation.repeatCount = HUGE //无限重复
        momAnimation.isRemovedOnCompletion = false //切出此界面再回来动画不会停止
        懒得起名的伊凡:@RichieVincent 切换页面回来动画停止,是否是执行时间已经结束了,本来就该停止了,将时间设置长点测试一下
      • 就是一个春天的花朵:点击button执行动画,为什么执行一次再次点击就不会执行第二次了
      • Shawn_Wang:感谢分享!最近在看一个怎么实现iOS下载app的动画,困扰的是怎么让动画旋转后消失,发现原来设置两个属性就可以了。谢谢
      • is_Onlooker:我让一张图片一直旋转,只要我获取到后台给我的值(0.失败 1.成功 ),就停止动画,且换上成功(对勾图)或失败(叉图)相对应的图上去。但是有一个问题:对勾图或叉图会旋转一定的角度显示上去(歪的),请问如何复原图片的位置?(补充一下,我换图片是在动画停止后换的)
      • 呵呵哈哈嘿嘿:请问,如何让动画提前结束?而且停止后位置是动画停止那一刻的位置?
        懒得起名的伊凡:使用调用CALayer的removeAnimationForKey:方法结束动画,使用presentationLayer获取当前属性的值并进行设置
      • 目染江夏:在动画结束的代理方法中 ,通过 key-value对查询不到呀!
      • 115ccb264d5e:学习了 表示感谢
      • ZhangCc_:已收藏
      • a713c420aece:太赞了,很详细。最近在做动画你的文章让我获益匪浅,谢谢
      • Jarvan_Sun:mark 学习了
      • 91f644c8e274:厉害了我的哥
      • 我七岁就很帅:这是值是在哪里找到的,我查了半天API也没找出来,求告知。
      • 码出我天地:补充:注意一下CABasicAnimation的delegate是strong属性,容易导致循环引用而不能将内存释放
        懒得起名的伊凡:@DoingLiaw 感谢提醒,之前还真没注意到这个问题,补充了一下
      • CodingIran:旋转能否指定旋转中心?而不是默认绕着固定的X Y Z中轴
        SunshineTeemo:你想要的可以去找关键帧动画
      • 星夜虫:帮了大忙了
        懒得起名的伊凡:@纪春 能帮到忙很荣幸
      • degulade:厉害!

      本文标题:CABasicAnimation使用总结

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