在结束上文的动画综述之后,这篇文章我们来看看 Core Animation 中的另一部分 Layer。作为框架的核心内容之一,CALayer 及其子类作为视图界面的基石不仅在绘图上性能强大而且功能性亦不可小觑。当然 Layer 部分的内容非常繁杂,每一个类型都进行讲解既不现实也不高效。文章将选取几个与动画关系较为密切的图层作为切人点,剩余部分留给大家探索(不要停留在想象阶段)。

CAGradientLayer

CAGradientLayer 是用来生成两种或更多颜色平滑渐变的。虽然图层本身并没有动画效果,但是我们可以利用渐变颜色构造隐式动画,典型的示例就是 iPhone 的滑动解锁。

为了实现渐变效果我们需要设置以下的属性值:

  • startPoint:渐变起始点,默认 (0, 0) 表示左上角。

  • endPoint:渐变结束点,默认 (1, 1) 表示右下角。

  • colors:渐变的颜色数组。

  • locations:可选值,默认为 nil 也就是均匀渐变。手动设置的时候需要注意,其中个数需与 colors 保持一致。

下面直接上码:

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor.gray
    
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = CGRect.init(x: 50, y: 100, width: 200, height: 40)
    
    gradientLayer.colors = [
        UIColor.black.cgColor,
        UIColor.white.cgColor,
        UIColor.black.cgColor
    ]
    gradientLayer.startPoint = CGPoint.init(x: 0, y: 0.5)
    gradientLayer.endPoint = CGPoint.init(x: 1, y: 0.5)
    gradientLayer.locations = [0.25,0.5,0.75];
    
    view.layer.addSublayer(gradientLayer)
    
    let gradientAnimation = CABasicAnimation(keyPath: "locations")
    gradientAnimation.fromValue = [0.0, 0.0, 0.25]
    gradientAnimation.toValue = [0.75, 1.0, 1.0]
    gradientAnimation.duration = 3.0
    gradientAnimation.repeatCount = 100
    unlock = UILabel.init(frame: gradientLayer.bounds)
    unlock?.alpha = 0.5;
    unlock?.text = "滑动来解锁 >>"
    unlock?.textAlignment = .center;
    gradientLayer.mask = unlock?.layer;
    gradientLayer.add(gradientAnimation, forKey: nil)

}

CAGradientLayer

上面的代码中,在新建图层后设置了渐变颜色集合,紧接着设置 startPointendPoint 属性指定渐变路径,最后指定了颜色集的渐变位置。完成了图层设置后我们添加了简单的位移动画让渐变色带移动起来,并添加了文字 Label。

CAShapeLayer

CAShapeLayer 是一个通过矢量图形而不是 bitmap 来绘制的图层子类。你指定诸如颜色和线宽等属性,用 CGPath 来定义想要绘制的图形,最后 CAShapeLayer 就自动渲染出来了。当然,你也可以用 Core Graphics 直接向原始的 CALyer 的内容中绘制一个路径,相比直下,使用 CAShapeLayer 有以下一些优点:

  • 渲染快速。CAShapeLayer 使用了硬件加速,绘制同一图形会比用 Core Graphics 快很多。

  • 高效使用内存。一个 CAShapeLayer 不需要像普通 CALayer 一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。

  • 不会被图层边界剪裁掉。一个 CAShapeLayer 可以在边界之外绘制。

CAShapeLayer 常见于下拉刷新动画以及各种曲线绘制动画中。常用的属性有:

  • path:图层的外框路径。

  • fillColor:填充颜色。

  • lineWidth:线宽。

  • lineCap:边线风格。

  • lineDashPattern:可选类型,设置边线的虚实高度。例如:[2, 3] 就表示边线虚实相间,实线高为 3、虚线高为 2。

  • strokeStart:边线起始位置,有效取值范围是 0.0 ~ 1.0,但是有时候为了与 strokeEnd 一起形成特定效果也会设置为负数。

  • strokeEnd:边线结束位置,有效取值范围是 0.0 ~ 1.0。

下面我们看下拉刷新样式动画的 CAShapeLayer 实现:

override func viewDidLoad() {
    super.viewDidLoad()

    let ovalShapeLayer: CAShapeLayer = CAShapeLayer()
    ovalShapeLayer.path = UIBezierPath.init(ovalIn: CGRect.init(x: 100, y: 100, width: 80, height: 80)).cgPath
    ovalShapeLayer.fillColor = UIColor.clear.cgColor
    ovalShapeLayer.strokeColor = UIColor.blue.cgColor
    ovalShapeLayer.lineWidth = 4
    ovalShapeLayer.lineDashPattern = [2,3]
    view.layer.addSublayer(ovalShapeLayer)
    
    let airplaneLayer = CALayer()
    let airplaneImage = UIImage(named: "airplane.png")!
    airplaneLayer.contents = airplaneImage.cgImage
    airplaneLayer.bounds = CGRect(x: 0.0, y: 0.0, width: airplaneImage.size.width, height: airplaneImage.size.height)
    airplaneLayer.position = CGPoint(x: 180, y: 140)
    view.layer.addSublayer(airplaneLayer)
    
    let strokeStartAnimation = CABasicAnimation(keyPath: "strokeStart")
    strokeStartAnimation.fromValue = -1.0
    strokeStartAnimation.toValue = 1.0
    
    let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
    strokeEndAnimation.fromValue = 0.0
    strokeEndAnimation.toValue = 1.0
    
    let strokeAnimationGroup = CAAnimationGroup()
    strokeAnimationGroup.duration = 1.5
    strokeAnimationGroup.repeatDuration = 5.0
    strokeAnimationGroup.animations = [strokeStartAnimation, strokeEndAnimation]
    ovalShapeLayer.add(strokeAnimationGroup, forKey: nil)
    
    let flightAnimation = CAKeyframeAnimation(keyPath: "position")
    flightAnimation.path = ovalShapeLayer.path
    flightAnimation.calculationMode = kCAAnimationPaced
    
    let airplaneOrientationAnimation = CABasicAnimation(keyPath: "transform.rotation")
    airplaneOrientationAnimation.fromValue = 0
    airplaneOrientationAnimation.toValue = 2 * M_PI
    
    let flightAnimationGroup = CAAnimationGroup()
    flightAnimationGroup.duration = 1.5
    flightAnimationGroup.repeatDuration = 5.0
    flightAnimationGroup.animations = [flightAnimation, airplaneOrientationAnimation]
    airplaneLayer.add(flightAnimationGroup, forKey: nil)
    
    view.backgroundColor = UIColor.gray
}

上面代码中,我们先创建了两个 Layer 对象并分别设置了属性、寄宿图和起始位置,然后在分别对两个图层添加了动画组。需要注意的细节是:ovalShapeLayer 动画组中的起始点和结束点的值设置不一样,其中 strokeStart 是从 -1 ~ 1 而 strokeEnd 则是 0 ~ 1 。将 strokeStart 起始位置后造成移动速度的增加从而在时间线上形成 strokeStart 追赶 strokeEnd 的动画效果。对于 airplaneLayer 来说,我们将其起始位置设置在 ovalShapeLayer 的右边垂直切线上以达到理想的效果。

CAShapeLayer

CAReplicatorLayer

CAReplicatorLayer 主要是为了高效生成许多相似的图层,可以复制自己子层的layer,并且复制出来的 layer 和原生子层有同样的属性,位置,形变,动画。一图胜千言,我们先来看效果图:

CAReplicatorLayer

我们通过 CAReplicatorLayer 将左边的 CALayer 的复制,然后将动画进行依次延迟。

override func viewDidLoad() {
    super.viewDidLoad()
    
    let replicatorLayer = CAReplicatorLayer()
    replicatorLayer.frame = CGRect.init(x: 100, y: 100, width: 90, height: 40)
    replicatorLayer.backgroundColor = UIColor.white.cgColor
    
    view.layer.addSublayer(replicatorLayer)
   
    let crcleBallLayer = CALayer()
    crcleBallLayer.backgroundColor = UIColor.green.cgColor
    crcleBallLayer.frame = CGRect.init(x: 0, y: 10, width: 20, height: 20)
    crcleBallLayer.cornerRadius = 10
    
    let basicAniamtion = CABasicAnimation.init(keyPath: "transform.scale")
    basicAniamtion.fromValue = 1.0
    basicAniamtion.toValue = 0.2
    basicAniamtion.duration = 0.5
    basicAniamtion.autoreverses = true
    basicAniamtion.repeatCount = 100
    crcleBallLayer.add(basicAniamtion, forKey: nil)
    
    replicatorLayer.addSublayer(crcleBallLayer)
    
    replicatorLayer.instanceCount = 3       // 复制个数
    replicatorLayer.instanceDelay = 0.3     // 动画延迟
    // 每一个 CALayer 都与前面位移差 30
    replicatorLayer.instanceTransform = CATransform3DTranslate(CATransform3DIdentity, 30, 0, 0 )    
    
}

总结

这篇文章对几个图层进行了简单的介绍并配上了代码示例,到此 Core Animation 部分的总结也到此告一段落。当然了 Core Animation 部分的内容深度远不是几篇简单的文章能够概括的,不过我还是希望自己的学习总结能够给读者带来些许帮助。这是最坏的时代也是最好的时代,一切都取决于我们自己。


BigNerdCoding
1.2k 声望125 粉丝

个人寄语: