美文网首页iOS资料swift核心动画
iOS动画指南 - 2.Layer Animations的基本使

iOS动画指南 - 2.Layer Animations的基本使

作者: Dariel | 来源:发表于2016-06-19 14:33 被阅读2361次

    上篇View Animations可以实现一些简单的动效,但并不能够完全满足开发使用,自定义程度比较低,为此我们来学习下Layer Animations.
    那么Layer有哪些可以自定义动画的属性呢?

    • 位置和尺寸: bounds position transform
    • 边框: borderColor borderWidth cornerRadius
    • 阴影: shadowOffset shadowOpacity shadowPath shadowRadius
    • 内容: contents mask opacity

    等等,以上仅仅只是一部分.

    1.开篇

    还是先从位移说起吧!还记得在上一篇View Animations中是怎么实现的吗?
    现在我们可以这样做:

            let flyRight = CABasicAnimation(keyPath: "position.x")
            // 开始时的位置
            flyRight.fromValue = 80
            // 结束时的位置
            flyRight.toValue = 230
    
            flyRight.duration = 0.5
            // 延迟0.3执行
            flyRight.beginTime = CACurrentMediaTime() + 0.3
            // 添加到dogImageView上
            dogImageView.layer.addAnimation(flyRight, forKey: nil)
    

    这边会碰到一个问题,当动画结束后dogImageView会立即返回初始的状态,那么如果要dogImageView保持在结束状态该怎样做呢?

            let flyRight = CABasicAnimation(keyPath: "position.x")
            // 保证fillMode起作用
            flyRight.removedOnCompletion = false
            // 动画结束后,layer会保持结束状态
            flyRight.fillMode = kCAFillModeForwards
            flyRight.fromValue = 60
            flyRight.toValue = 230
            flyRight.duration = 0.5
            // 延迟执行
            flyRight.beginTime = CACurrentMediaTime() + 1
    
            dogImageView.layer.addAnimation(flyRight, forKey: nil)
    

    设置fillMode的属性就行了.

    fillMode的几个值的分别代表的含义:

    • kCAFillModeRemoved : 默认样式 动画结束后会回到layer的开始的状态
    • kCAFillModeForwards : 动画结束后,layer会保持结束状态
    • kCAFillModeBackwards : layer跳到fromValue的值处,然后从fromValue到toValue播放动画,最后回到layer的开始的状态
    • kCAFillModeBoth : kCAFillModeForwards和kCAFillModeBackwards的结合,即动画结束后layer保持在结束状态

    fillMode = kCAFillModeBoth 演示效果:

            dogImageView.layer.position.x = 80
    
            let flyRight = CABasicAnimation(keyPath: "position.x")
            // 保证fillMode起作用
            flyRight.removedOnCompletion = false
            flyRight.fillMode = kCAFillModeBoth
            flyRight.fromValue = 100
            flyRight.toValue = 230
            flyRight.duration = 0.5
            flyRight.beginTime = CACurrentMediaTime() + 1
            dogImageView.layer.addAnimation(flyRight, forKey: nil)
    

    dogImageView的初始位置为80,然后跳到100,从100开始执行动画,停在最终位置.
    左面是控制台,我们给dogImageView添加了一个点击监听,发现dogImageView的位置表面上看起来是改变了,其实它还在原来的位置,也就是说layer动画并不是真实的,如果要变成真实的需要改变其position,那问题来了这个效果的使用场景是什么?
    答:视图不需要交互,且动画的开始和结束需要设置特殊的值.

    2.代理

    如果我想在动画开始和结束的时候分别搞点事情该怎样做呢?对的,可以用代理呀!
    设置下代理就能实现代理方法了.

    
       flyRight.delegate = self
    
    extension ViewController { 
        override func animationDidStart(anim: CAAnimation) {
            print("动画开始调用")      
        }
        override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
            print("动画结束调用")
        }
    }
    

    使用Layer Animations可以设置好一个Animation,然后添加到不同的view上面,但这时如果我们设置了flyRight的代理,并将flyRight添加到了很多的view上面,那该怎样才能在代理方法中区分哪个是哪个呢?
    用KVC设置一个name然后在代理中做一下判断就行了.

            // 设置名称
            flyRight.setValue("form", forKey: "name")
            // 方便代理中可以拿到layer
            flyRight.setValue(dogImageView.layer, forKey: "layer")
    

    在动画结束时做一个特效

    extension ViewController {
        
        override func animationDidStart(anim: CAAnimation) {
            print("动画开始调用")
            
        }
        override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
            print("动画结束调用")
            
            if let name = anim.valueForKey("name") as? String {
                if name == "form" 
                    print("name is form")
                    
                    // 动画结束后设置一个先放大然后缩小的效果
                    let layer = anim.valueForKey("layer") as? CALayer
                    anim.setValue(nil, forKey: "layer")
                    
                    let pulse = CABasicAnimation(keyPath: "transform.scale")
                    pulse.fromValue = 1.25
                    pulse.toValue = 1.0
                    pulse.duration = 3.25
                    layer?.addAnimation(pulse, forKey: nil)         
                }
            }
        }
    }
    

    3.在动画的执行过程中移除动画

    在设计交互中,常常会碰到这样的一个问题,在动画的执行过程中,由于用户的点击操作,需要提前终止动画,该怎么实现呢?很简单

            // forKey以前基本是设置为nil的,现在派上用场了.因为dogImageView上可能添加了很多动画,forKey就是为了区别哪一个
            dogImageView.layer.addAnimation(flyRight, forKey: "position")
            // 添加到需要终止的地方,就可以随时终止动画了
            dogImageView.layer.removeAnimationForKey("position")
            // 移除dogImageView上所有动画
            dogImageView.layer.removeAllAnimations()
    

    4.动画组

    还记得View Animations中的动画组吗?在Layer Animations依然有这个,只不过这次添加进去的动画是同时执行的.

            let groupAnimation = CAAnimationGroup()
            // 延迟1秒
            groupAnimation.beginTime = CACurrentMediaTime() + 1
            // 整个动画持续3秒
            groupAnimation.duration = 3
            
            groupAnimation.removedOnCompletion = false
            groupAnimation.fillMode = kCAFillModeBoth
            
            // 缓慢加速缓慢减速
            groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            // 重复次数
            groupAnimation.repeatCount = 4.5
            // 来回往返执行
            groupAnimation.autoreverses = true
            // 速度
            groupAnimation.speed = 2.0
            
            let scaleDown = CABasicAnimation(keyPath: "transform.scale")
            scaleDown.fromValue = 1.5
            scaleDown.toValue = 1.0
            
            let roate = CABasicAnimation(keyPath: "transform.rotation")
            roate.fromValue = CGFloat(M_PI_4)
    
            roate.toValue = 0.0
            
            let fade = CABasicAnimation(keyPath: "opacity")
            fade.fromValue = 0.5
            fade.toValue = 1.0
            
            groupAnimation.animations = [scaleDown, roate, fade]
            dogImageView.layer.addAnimation(groupAnimation, forKey: nil)
    
    

    5.Layer的弹簧效果

    还记得前面View Animations中的弹簧效果吗?底层实际上就是通过下面要介绍的CASpringAnimation来实现.
    CASpringAnimation是CABasicAnimation的子类,用于实现弹簧动画。

    CASpringAnimation的重要属性:

    • mass:质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)
    • stiffness:弹性系数(弹性系数越大,弹簧的运动越快)
    • damping:阻尼系数(阻尼系数越大,弹簧的停止越快)
    • initialVelocity:初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)
    • settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)
            let scaleDown = CASpringAnimation(keyPath: "transform.scale")
            scaleDown.fromValue = 1.5
            scaleDown.toValue = 1.0
            
            // settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)
            scaleDown.duration = scaleDown.settlingDuration
            // mass:质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大) 默认值为1
            scaleDown.mass = 10.0
            // stiffness:弹性系数(弹性系数越大,弹簧的运动越快)默认值为100
            scaleDown.stiffness = 1500.0
            // damping:阻尼系数(阻尼系数越大,弹簧的停止越快)默认值为10
            scaleDown.damping = 50
            // initialVelocity:初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)默认值为0
            scaleDown.initialVelocity = 100
            
            dogImageView.layer.addAnimation(scaleDown, forKey: nil)
    

    5.总结

    这一篇介绍了CABasicAnimation,以及子类CAAnimationGroup,CASpringAnimation的基本使用.

    本文整理自 : iOS.Animations.by.Tutorials.v2.0
    源码 : https://github.com/DarielChen/DemoCode
    如有疑问,欢迎留言 :-D

    相关文章

      网友评论

      • 路过河边:如果几个view 同时做动画怎么搞
      • 横渡:最后一个弹簧动画,我试的时候,图片开始的一瞬间发生了翻转,这是为什么呢?
        横渡:@Dariel在杭州 参数和你文章中的一样
        Dariel:@横渡 那应该是参数给的有问题
      • 鱼鱼鱼四只鱼:这本书好贵
      • 萧城x:位移动画
      • Dayu大鱼:简单易懂 是给别人看的好文章
      • ___as7:大赞....

      本文标题:iOS动画指南 - 2.Layer Animations的基本使

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