【iOS动画】学习笔记第二弹(Layer Animation)

本文是笔者学习iOS动画的一些小总结,接第一弹;

Layer Animation

第一弹中主要是关于View Animation 的一系列操作,今天的主角当然得是Layer啦,其实Layer Animation 工作并不复杂,我们只需要选择一个是animatable的属性,然后设置开始值、结束值、动画时间,之后系统就会让Core Animation去对应的layer渲染,产生动画效果。

CALayer相比UIView有更多的animatable的属性,所以使用Layer Animation可以更好的写出自己想要的动画效果,同时CALayer还有很多特定的子类,常见的如下所示:CAShapeLayer, CATextLayer, CAGradientLayer, CAReplicatorLayer….. 这些不同的子类,又包含不同的animatable的属性,所以根据特定的子类,可以简单的写出很酷炫的动画。下面来看一个栗子:

 let springMoveLeftAnimation = CABasicAnimation(keyPath: "position.x")
 springMoveLeftAnimation.fromValue = -view.frame.width
 springMoveLeftAnimation.toValue = view.frame.width / 2.0
 springMoveLeftAnimation.duration = 0.7
 springMoveLeftAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
 colorView.layer.add(springMoveLeftAnimation, forKey: nil)

上述栗子创建了一个springMoveLeftAnimation,这个动画对象是可以加到任意多个layer上去的,所以使用Layer Animation 创建的动画是可以重复利用的。上述栗子中只是简单的将一个view从左移动到屏幕中间。

真正的动画

上栗中,我们在colorView的layer上增加了一个动画,其实我们看到的动画并不是真正作用在colorView,我们看到的只是一个所谓的presentation layer,当动画结束后presentation layer就会在屏幕中被移除,真正的colorView重新显示到屏幕上。

所以上栗中,colorViewx原本不是居中的,经过动画后,colorViewx依然不是居中的,会移动到原本的位置。

所以想要colorViewx保持动画后的模样,我们可以设置如下代码:

springMoveLeftAnimation.fillMode = kCAFillModeBoth
springMoveLeftAnimation.isRemovedOnCompletion = false
fillMode

fillModeCAMediaTiming协议中一个属性,用来控制动画序列的开始和结束的行为,默认是kCAFillModeRemoved,默认效果如下图所示:

【iOS动画】学习笔记第二弹(Layer Animation)_第1张图片
屏幕快照 2017-09-20 15.36.00.png

如果想要延时执行某个动画,可以设置beginTime属性

springMoveLeftAnimation.beginTime = CACurrentMediaTime() + 0.3 
/// 根据CACurrentMediaTime()取得动画执行的时间,然后我们增加了0.3秒的延时
  • kCAFillModeBackwards
【iOS动画】学习笔记第二弹(Layer Animation)_第2张图片
屏幕快照 2017-09-20 15.43.24.png

如图所示,设置fillModekCAFillModeBackwards,不论是否设置延时,都会提前显示动画的第一帧。

  • kCAFillModeForwards

【iOS动画】学习笔记第二弹(Layer Animation)_第3张图片
屏幕快照 2017-09-20 15.46.24.png

如图所示,设置 fillModekCAFillModeForwards,当动画被移除之前会保留动画的最后一帧。

  • kCAFillModeBoth

【iOS动画】学习笔记第二弹(Layer Animation)_第4张图片
屏幕快照 2017-09-20 15.49.05.png

如图所示,设置 fillModekCAFillModeBoth,相当于是上面两个属性的结合。

当动画结束后会保留最后一帧,然后设置isRemovedOnCompletionfalse,所以动画就不会被移除,这样colorView就能够保持动画后的模样。但是现在因为不是真正的colorView,所以就不能做任何操作了,当colorView为输入框时,此时因为是presentation layer,也就不能响应用户的输入。而且这样做也会有性能问题,所以不建议设置isRemovedOnCompletionfalse

此时还有一个有效的方法是,通常我们可以在colorView加入动画后,直接更新colorViewx,这样在动画结束后,colorView的位置就保持和动画后一致。

控制动画

第一弹中我们通过UIKit帮我们封装的UIView语法的动画一旦创建运行,我们是不能暂停或者停止的。但是Layer Aniamtion提供了相关的API,让我们可以更近一步的去控制我们所创建的动画。

animation delegate

我们可以设置CAAnimation的代理,通过代理提供的方法来控制动画。

 func animationDidStart(_ anim: CAAnimation)
 func animationDidStop(_ anim: CAAnimation, finished flag: Bool)

CAAnimationDelegate提供了上述两个代理方法,通过提供的anim参数从而可以控制动画,但是如果多个动画都设置了代理,这时如何区分不同的动画做不同的事情呢?

因为CAAnimation 和其子类是用OC写的,而且支持KVC,所以我们可以用func setValue(_ value: Any?, forKey key: String)方法来设置不同key给不同的动画,这样在代理中就能区别不同的动画了。

通过设置代理的方式我们只能控制刚开始和刚结束时期的动画,那么如何控制正在运行的动画呢,此时就需要用到func add(_ anim: CAAnimation, forKey key: String?)方法中的key参数,我们可以在该动画开始后通过设置的key参数来控制对应的动画了。

 open func removeAllAnimations()
 open func removeAnimation(forKey key: String)

我们可以通过以上两个方法来对操作正在运行的动画,哈哈,只是简单的移除
并不能动态的改变动画运行路径。

我们可以设置动画的speed属性控制动画速度,同时也可以通过设置layer的speed属性从而使layer上添加的所有动画设置速度,此时如果layer里某个动画自身也设置了速度,那么此时的速度会根据view的层级进行乘积;

组合动画

我们可以在layer上添加多个动画,如果想要控制不同动画之间的同步,此时需要用到CAAnimationGroup

    // added animation group
    let groupAnimation = CAAnimationGroup()
    groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
    groupAnimation.beginTime = CACurrentMediaTime() + 0.5
    groupAnimation.duration = 0.5
    groupAnimation.fillMode = kCAFillModeBackwards
    
    let scaleDown = CABasicAnimation(keyPath: "transform.scale")
    scaleDown.fromValue = 3.5
    scaleDown.toValue = 1.0
    let rotate = CABasicAnimation(keyPath: "transform.rotation")
    rotate.fromValue = .pi / 4.0
    rotate.toValue = 0.0
    let fade = CABasicAnimation(keyPath: "opacity")
    fade.fromValue = 0.0
    fade.toValue = 1.0
    
    groupAnimation.animations = [scaleDown, rotate, fade]
    loginButton.layer.add(groupAnimation, forKey: nil)

基本的layer animation 差不多就是这些,可能不是面面俱到,但是其他的一些属性,可以通过查看头文件里的属性即可,下面学习下layer animation里的spring animation;

弹性动画

第一弹中我们就已经接触过layer animation,但是UIKit帮我们封装了,并没有深入的了解其中的细节,所以在layer 层中的spring animation我们可以进一步的研究下spring animation 的细节。

首先我们想象下钟摆,当我们给它一个力,此时如果没有摩擦的话,那么钟摆就会永远的摆动,但是实际由于和空气之间产生摩擦,所以钟摆最后一定会停止。而且不同质量的钟摆从运动到停止的时间是不一样的,最后还和重力有关系,同样的钟摆在月球上的运动轨迹和地球是有很大的差距的。

其实钟摆来回摆动的这个效果就是spring animation 的效果。此时我们可以根据上述总结出如下属性是影响spring animation的:

1. damping: 摩擦相关, 默认是10.0
2. mass: 质量,默认是1.0
3. stiffness: 重力相关, 默认是100.0
4. initialVelocity: 初始速度,默认是0.0

上述的四个属性就是CASpringAnimation所提供的,其中damping,initialVelocity我们在UIKit提供的spring animation 中就已经使用过,UIKit会根据我们所给的duration来计算出其他两个属性的,从而产生对应的spring animation,所以有时候会感觉有点不真实。但我们现在可以使用CASpringAnimation所提供的四个属性来创建自己想要的同时更加真实的spring animation, 但是这种方式的缺点是,时间是不确定的,因为spring animation 从开始到停止的时间是根据你所提供的四个影响参数来计算出的。

所以我们设置spring animation的 duration时,需要使用spring animation的settlingDuration属性,这个时间就是根据你所提供的四个影响参数来计算出的;

            let flash = CASpringAnimation(keyPath: "borderColor")
            flash.damping = 7.0
            flash.stiffness = 200.0
            flash.fromValue = UIColor(red: 1.0, green: 0.27, blue: 0.0, alpha: 1.0).cgColor
            flash.toValue = UIColor.white.cgColor
            flash.duration = flash.settlingDuration
            textField.layer.add(flash, forKey: nil)

上述代码创建了一个spring animation 改变文本输入框的borderColor,要想做出自己想要的spring 动画,只需要不断的调整上述的四个属性即可。

keyframe动画

UIKit 也提供了keyframe animation,但是和layer层的keyframe animation 相比是有区别的。UIKit的keyframe animation,是用来做动画的连接的,可以在animations中创建多个keyframe动画,这些动画可以是设置在不同的view的不同属性。

之前我们设置在layer上的basic animation, 我们都会设置fromValuetoValue,通过设置的duration,Core Animation会自动的根据设置的value在给定的时间内改变layer的某个属性,于是动画产生了,而layer的keyframe animation则提供了让我们自己控制动画某个属性的过程的功能:

    let flight = CAKeyframeAnimation(keyPath: "position")
    flight.duration = 12.0
    flight.values = [
      CGPoint(x: -50, y: 0.0),
      CGPoint(x: view.frame.width + 50.0, y: 160.0),
      CGPoint(x: -50.0, y: loginButton.center.y)]
        .map { NSValue(cgPoint: $0) }
    
    flight.keyTimes = [0.0, 0.5, 1.0]
    balloon.add(flight, forKey: nil)

如上代码所示,我们通过values,keyTimes,自己可以控制动画的运行轨迹。

当创建的layer animation的keyPath是结构体时,需要使用NSValue进行包装;

Specialized Layers

前面所说的Layer Animation,我们都是为CALayer的一些基本属性做动画的,但是如果想要做出一些其他的酷炫的动画,哪些基本的可能满足不了要求,好在CALayer有很多有用的子类,我们通过它们的属性做动画,效果就会大不一样。

CAShapeLayer

CAShapeLayer通过你传入的pathvector graphics来画你所定义的的图形。下面看个栗子:

        let squarePath = UIBezierPath(rect: bounds)
        let animation = CABasicAnimation(keyPath: "path")
        animation.duration = 0.25
        animation.fromValue = circleLayer.path
        animation.toValue = squarePath.cgPath
        circleLayer.add(animation, forKey: nil)
        circleLayer.path = squarePath.cgPath
        
        maskLayer.add(animation, forKey: nil)
        maskLayer.path = squarePath.cgPath

和之前的layer animation一样,同样的设置fromValuetoValue,不过这次animation 的keyPath为CAShapeLayerpath属性。

CAShapeLayerstrokeEnd属性同样是animatable,所以根据这个属性可以做出一个画的过程的动画:

        let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
        pathAnimation.duration = 10.0
        pathAnimation.fromValue = 0.0
        pathAnimation.toValue = 1.0
        pathLayer?.add(pathAnimation, forKey: "strokeEnd")

完整的Demo下载地址CAShapeLayerAnimationDemo

CAShapeLayer还有一个功能就是设置圆角,只需要设置一个shapeLayer作为layer 的mask属性即可。

CAGradientLayer

CAGradientLayer可以通过设置多种颜色画出新的渐变的效果,而且CAGradientLayer有四个属性是animatable的:

startPoint, endPoint 为单元坐标系

colors: 渐变效果的颜色数组;
locations: 每种颜色的在渐变中的占比;
startPoint: 开始的点;
endPoint: 结束的点;

iOS之前的滑动进行解锁的动画就可以使用CAGradientLayer来实现:

WechatIMG1.png

完整的Demo下载地址CAGradientLayerAnimationDemo

CAReplicatorLayer

CAReplicatorLayer通常用来生成重复layer的集合,这样比手动添加效率更高。但是CAReplicatorLayer可以轻松的改变每个克隆layer的属性,让他们和他们的父亲不一样。最后CAReplicatorLayer有一个特别的属性instanceDelay,当你设置该属性为0.1秒同时在原layer上加了一个动画,此时CAReplicatorLayer生成的第一份克隆会延迟0.1秒执行动画,第二份克隆则会延迟0.2秒,第三份克隆则会延迟0.3秒,以此类推。通过这个特性我们可以写出一些复杂的动画效果。以下是CAReplicatorLayer三个重要的属性:

instanceCount: 设置你想要的克隆个数;
instanceTransform: 设置每个克隆和上一个克隆的差距;
instanceDelay: 设置每个克隆和上一个克隆的动画延迟;

利用CAReplicatorLayer上述特性,我们可以发挥想象做出一些酷炫的动画,例如下面这个动画:

【iOS动画】学习笔记第二弹(Layer Animation)_第5张图片
WechatIMG3.jpeg

完整的Demo下载地址CAReplicatorLayerAnimationDemo

最后

未完待续第三弹

你可能感兴趣的:(【iOS动画】学习笔记第二弹(Layer Animation))