Swift - CAReplicatorLayer的使用

CAReplicatorLayer是CoreAnimation框架中的一个容器类,它有别于普通的如CAShapeLayer的Layer,主要是用于复制其sublayers并以特定的方式排列这些sublayers,做出一些有意思的动画效果。自定义Loading动画时就经常用到CAReplicatorLayer

环境:Swift 3.0, Xcode 8.2
GitHub源码:https://github.com/Hesse-Huang/CAReplicatorLearning

效果图如下:


CAReplicatorLayer.gif

分析:
1.绿色圆点
首先圆点是给到CAReplicatorLayersublayers中的,因此需要是个CALayer对象。虽然效果图中的64个点,但是不用真的做64个对象——它们是被CAReplicatorLayer复制而来的。既然是1个点那就不难了,用CAShapeLayer + UIBezierPath搞定。

// 在viewDidLoad()中
self.dotLayer = CAShapeLayer()
dotLayer.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
dotLayer.path = UIBezierPath(ovalIn: dotLayer.bounds).cgPath
dotLayer.fillColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1).cgColor

2.CAReplicatorLayer复制这些绿色圆点
这里就该是主角出场了。设置一下instanceCountinstanceDelayinstanceTransform即可。这里说明一下instanceTransform属性:它控制这些instance的排列方式(或者说是而布局方式),需要传入一个CATransform3D结构体。CATransform3DCGAffineTransform有点像,无非就是平移、旋转和缩放三种效果,但使用时需要用回诸如CATransform3DMakeTranslation(_:_:_:)这样的那些传统的全局函数。
第n+1个圆点的位置是根据第n个圆点的坐标系确定的,因此如果是水平方向上平移,则是 Pn+1.x = Pn.x + xOffset。如果变换中涉及旋转,那就得要注意了,角度也是会累积的哦。

let replicatorLayerX = CAReplicatorLayer()
replicatorLayerX.frame = CGRect(x: 60, y: 100, width: 30, height: 30)  // 1️⃣
replicatorLayerX.instanceCount = 8      // 实例数,这里8个
replicatorLayerX.instanceDelay = 0.1    // 每个实例之间动画延迟
replicatorLayerX.instanceTransform = CATransform3DMakeTranslation(32, 0, 0)     // 每个实例间的排列方式
replicatorLayerX.addSublayer(dotLayer)

view.layer.addSublayer(replicatorLayerX)  // 2️⃣

Run一下,可以看到这样的效果。


CAReplicatorLayer2.gif

3.实现8x8矩阵
我们再用一个CAReplicatorLayer再包装一下上面的replicatorLayerX,让它在垂直方向上排列,就实现了效果图中的实现8x8矩阵了。

let replicatorLayerY = CAReplicatorLayer()
replicatorLayerY.frame = CGRect(x: 60, y: 100, width: 30, height: 30)
replicatorLayerY.instanceCount = 8
replicatorLayerY.instanceDelay = 0.1
replicatorLayerY.instanceTransform = CATransform3DMakeTranslation(0, 32, 0)
replicatorLayerY.addSublayer(replicatorLayerX)

view.layer.addSublayer(replicatorLayerY)

记得注释掉1️⃣和2️⃣两行代码。

最后,因为圆点有缩放和透明度变化两个动画,我们需要有这两个动画对象,用CAAnimationGroup加入到dotLayer中。

var scaleAnimation: CABasicAnimation {
    let a = CABasicAnimation(keyPath: "transform")
    let t = CATransform3DIdentity
    a.fromValue = NSValue(caTransform3D: CATransform3DScale(t, 0.5, 0.5, 0.5))
    a.toValue = NSValue(caTransform3D: CATransform3DScale(t, 1, 1, 1))
    return a
}

var opacityAnimation: CABasicAnimation {
    let a = CABasicAnimation(keyPath: "opacity")
    a.fromValue = 1.0
    a.toValue = 0.2
    return a
}

@IBAction func startAnimation(_ sender: UIButton) {
    let ag = CAAnimationGroup()
    ag.animations = [scaleAnimation, opacityAnimation]
    ag.duration = 0.8
    ag.autoreverses = true
    ag.repeatCount = 10000
    dotLayer.add(ag, forKey: "groupAnimation")
}

最终效果请看原码哦,喜欢请点赞,交流请评论~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容