CAReplicatorLayer
是CoreAnimation框架中的一个容器类,它有别于普通的如CAShapeLayer
的Layer,主要是用于复制其sublayers
并以特定的方式排列这些sublayers
,做出一些有意思的动画效果。自定义Loading动画时就经常用到CAReplicatorLayer
。
环境:Swift 3.0, Xcode 8.2
GitHub源码:https://github.com/Hesse-Huang/CAReplicatorLearning
效果图如下:
分析:
1.绿色圆点
首先圆点是给到CAReplicatorLayer
的sublayers
中的,因此需要是个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复制这些绿色圆点
这里就该是主角出场了。设置一下instanceCount
、instanceDelay
和instanceTransform
即可。这里说明一下instanceTransform
属性:它控制这些instance的排列方式(或者说是而布局方式),需要传入一个CATransform3D
结构体。CATransform3D
跟CGAffineTransform
有点像,无非就是平移、旋转和缩放三种效果,但使用时需要用回诸如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一下,可以看到这样的效果。
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")
}
最终效果请看原码哦,喜欢请点赞,交流请评论~