引言
本篇博客我们来详细的讨论一下CAReplicatorLayer图层,它是一个功能非常强大的工具,允许我们通过重复图层来创建复杂且高效的动画效果,无论是加载动画,粒子效果,还是其它重复性的图形动画,CAReplicatorLayer都能轻松实现。
接下里,我们就来深入的探讨一下如何使用CAReplicatorLayer来构建自定义的loading动画。我们将从基础概念入手,逐步构建一个简单的loading动画,并探讨一些优化和扩展的方法。希望通过这篇文章,你能对CAReplicatorLayer有更深的理解,并能够应用它来打造出色的动画效果。
基础
CAReplicatorLayer的作用是可以高效的生成许多相似的图层。它会绘制一个或多个图层的子图层,并且在每个复制体上应用不同的变换。
图层属性
CAReplicatorLayer有几个重要的属性,每个属性都可以用来定制复制图层的效果和动画:
- instanceCount:最常用的属性,它指定了创建的副本的数量,默认值为1,也就是不创建副本。
- instanceDelay:该属性指的是每个副本相对于前一个副本的延迟时间,比如当我们做动画时,CAReplicatorLayer产生的副本并不会同时进行动画,而是有一定延迟(所以很适合做loading)。
- instanceTransform:该属性是一个CATransform3D类型的值,用于指定每个副本相对于前一个副本做的变换(记住是相对前一个副本奥)。
- instanceColor:该属性指的是副本图层的颜色,如果副本的颜色需要与原始图层不同,则需要设置该属性。
- instanceRedOffset:该属性指定了副本图层的红色通道偏移量,用于改变副本的颜色。
- instanceGreenOffset:指定副本图层的绿色通道偏移。
- instanceBlueOffset:指定副本图层的蓝色通道偏移量。
- instanceAlphaOffset:指定副本图层的透明度偏移量。
- preservesDepth:该属性指定是否保留图层的深度信息,默认值是NO,如果设置为YES,则副本会保留3D变换的深度信息。
图层使用
我们接下来通过一个简单的示例来展示CAReplicatorLayer的基础用法。
1.创建CAReplicatorLayer,并添加到视图的图层中:
/// 重复图层
let replicatorLayer = CAReplicatorLayer()
replicatorLayer.frame = self.bounds
self.layer.addSublayer(replicatorLayer)
我们创建了一个复制图层,设置其大小等于视图的大小,并将它添加到了视图的图层当中。
2.添加子图层
接下来我们需要创建一个子图层,然后它才会被CAReplicatorLayer复制,为了展示效果,我们就来创建一个简单的小圆点。
/// 圆点
let dotLayer = CALayer()
dotLayer.frame = CGRect(x: self.bounds.width / 2 - 5, y: 0, width: 10, height: 10)
dotLayer.backgroundColor = UIColor.red.cgColor
dotLayer.cornerRadius = 5
replicatorLayer.addSublayer(dotLayer)
这段代码我们,创建了一个圆形红色的小点,并将其添加到了CAReplicatorLayer中。
我们先创建出来看一下效果:
let loadingView = PHLoadingView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
self.view.addSubview(loadingView)
loadingView.center = self.view.center
我们创建了一个大小为40*40屏幕居中的视图,效果如下:
只有一个小红点,没有什么特别的地方,这是因为我们只是将图层添加到了复制图层上,但是并没有设置CAReplicatorLayer的任何属性,接下来我们就来开始体验每个属性给视图带来的变化。
3.配置CAReplicatorLayer的属性
首先我们来配置两个最基础的属性,instanceCount和instanceTransform,指定复制图层的个数并且让每个复制图层相对前一个图层进行变化,这样我们才能观察到每个复制图层:
replicatorLayer.instanceCount = 10
var transform = CATransform3DIdentity
transform = CATransform3DRotate(transform, CGFloat.pi*2/10, 0, 0, 1)
replicatorLayer.instanceTransform = transform
上面的代码中我们设置了复制图层的个数为10,并且每个图层进行CGFloat.pi*2/10弧度的偏移,那么10个刚好围成一圈,效果如下:
4.修改颜色
通常instanceColor属性需要和子图层的颜色相结合,然后再配合instanceRedOffset
、instanceGreenOffset
、instanceBlueOffset
和 instanceAlphaOffset四个属性共同作用来调整每个副本的颜色:
replicatorLayer.instanceColor = UIColor.yellow.cgColor
replicatorLayer.instanceRedOffset = -0.1
replicatorLayer.instanceGreenOffset = -0.1
replicatorLayer.instanceBlueOffset = -0.1
效果如下:
5.修改复制图层的动画延迟
instanceDelay属性的需要结合动画才能看出来,那我们为图层添加一个由小变大的动画,动画持续时间为1秒,并设置图层的instanceDelay为0.1,代码如下:
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.fromValue = 1
animation.toValue = 0.1
animation.duration = 1
animation.repeatCount = Float.greatestFiniteMagnitude
dotLayer.add(animation, forKey: nil)
我们来看一下效果:
由于延迟的存在使得每个复制图层轮流缩小,显得像是动起来了,这样我们最基础的加载动画就已经实现完成了。
进阶loading动画
上面我们非常轻松的就实现了一个简单的自定义loading动画,接下来我们使用CAReplicatorLayer继续实现一些常见的loading动画。
视频上传框
视频和图片上传的功能应该比较常见,为了让上传的动画更有趣味,有些设计团队会将loading显示在正在上传的边框上,效果如下:
整个动画的核心仍然是使用CAReplicatorLayer复制图层动画延迟的属性,下面我们来看一下代码,
1.创建图片,以及重复图层:
/// 图片
private let imageView = UIImageView()
/// 复制图层
private let replicatorLayer = CAReplicatorLayer()
imageView.frame = self.bounds
imageView.image = UIImage(named: "coreAnimation")
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 10.0
self.addSubview(imageView)
self.layer.addSublayer(replicatorLayer)
replicatorLayer.instanceCount = 10
replicatorLayer.instanceDelay = 0.1
replicatorLayer.instanceAlphaOffset = -0.1
replicatorLayer.frame = self.bounds
图片的大小和重复图层的大小都与父视图大小相同。并设置了重复图层的个数以及执行动画的时间延迟,和每个图层的透明度偏移量。
2.创建子图层添加到复制图层:
/// 圆点
private let dotLayer = CALayer()
dotLayer.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
let image = UIImage(named: "xingxing")
dotLayer.contents = image?.cgImage
我们设置子图层的大小为10*10,并设置了寄宿图。
3.创建一个曲线,并让子图层沿着曲线做关键帧动画:
bezierPath = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: self.bounds.size.width, height: self.bounds.size.height), cornerRadius: 0.0)
let pathAnimation = CAKeyframeAnimation(keyPath: "position")
pathAnimation.path = bezierPath.cgPath
pathAnimation.duration = 4.0
pathAnimation.repeatCount = Float.greatestFiniteMagnitude
dotLayer.add(pathAnimation, forKey: nil)
由于图层的延迟执行动画功能,就会出现类似加载的效果了。
绘制Logo动画
通常还有一些常见我们会绘制自己的LOGO当做动画,比如下面的效果:
1.绘制线
想要实现外面的小框,首先我们需要使用CGPath绘制出它的轮廓,然后使用CAShapeLayer图层进行填充,再为图层添加一个动画代码如下:
func addLineAnimation() {
let lineLayer = CAShapeLayer()
lineLayer.strokeColor = UIColor.red.cgColor
lineLayer.fillColor = UIColor.clear.cgColor
lineLayer.lineWidth = 2.0
self.layer.addSublayer(lineLayer)
let width = self.bounds.size.width
let height = self.bounds.size.height
// 创建一个路径
let path = UIBezierPath()
path.move(to: CGPoint(x: width/4.0, y: 0.0))
path.addLine(to: CGPoint(x: width/2.0, y: 10.0))
path.addLine(to: CGPoint(x: 0.0, y: 10.0))
path.addLine(to: CGPoint(x: 0.0, y: height))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: width, y: 10.0))
path.addLine(to: CGPoint(x: width/2.0, y: 10.0))
path.addLine(to: CGPoint(x: width*3/4.0, y: 0.0))
lineLayer.path = path.cgPath
let animaionGroup = CAAnimationGroup()
animaionGroup.duration = 3
animaionGroup.repeatCount = Float.greatestFiniteMagnitude
animaionGroup.isRemovedOnCompletion = false
animaionGroup.fillMode = .forwards
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1.0
animation.duration = 2
animaionGroup.animations = [animation]
lineLayer.add(animaionGroup, forKey: nil)
}
2.绘制点
使用普通的CALayer图层绘制点,并将其添加到CAReplicatorLayer图层,然后设置复制图层的属性,并为CALayer添加透明度和缩放的动画效果,代码如下:
func addDotAnimation() {
let width = self.bounds.size.width
// 复制图层
let replicatorLayer = CAReplicatorLayer()
replicatorLayer.frame = self.bounds
replicatorLayer.instanceCount = 3
replicatorLayer.instanceDelay = 0.3
replicatorLayer.instanceTransform = CATransform3DMakeTranslation(width/4.0, 0, 0)
self.layer.addSublayer(replicatorLayer)
let dotLayer = CALayer()
dotLayer.frame = CGRect(x: 7, y: 20, width: 8, height: 8)
dotLayer.backgroundColor = UIColor.red.cgColor
dotLayer.cornerRadius = 4
replicatorLayer.addSublayer(dotLayer)
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.fromValue = 1
animation.toValue = 0.1
animation.duration = 1
animation.repeatCount = Float.greatestFiniteMagnitude
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 1
opacityAnimation.toValue = 0.1
opacityAnimation.duration = 1
opacityAnimation.repeatCount = Float.greatestFiniteMagnitude
let group = CAAnimationGroup()
group.animations = [animation,opacityAnimation]
group.beginTime = 2
group.duration = 1
group.repeatCount = Float.greatestFiniteMagnitude
dotLayer.add(group, forKey: nil)
}
绘制文字动画
文字想要获取路径并不容易,但是我们可以借助CAReplicatorLayer实现另外一种闪动效果的文字load效果(在真机上效果会更好一些):
1.创建重复图层
/// 复制图层
private let replicatorLayer = CAReplicatorLayer()
self.layer.addSublayer(replicatorLayer)
replicatorLayer.frame = self.bounds
replicatorLayer.instanceCount = 10
replicatorLayer.instanceDelay = 0.1
replicatorLayer.instanceAlphaOffset = -0.1
仍然还是先创建了一个重复图层,并且设置了它的大小与父视图大小相同,设置它的重复个数为10动画延迟为0.1秒,另外我们调整了它的透明度偏移量,这就意味着每一个图层会比前面的图层更透明一些。
2.创建背景图层
/// 普通图层
private let backLayer = CALayer()
backLayer.frame = CGRect(x: 0, y: 0, width: self.bounds.size.width / 10.0, height: self.bounds.size.height)
backLayer.backgroundColor = UIColor.green.cgColor
replicatorLayer.addSublayer(backLayer)
创建了一个绿色背景图层,它的宽度为视图宽度的1/10。
3.创建文字图层
/// textLayer
private let textLayer = CATextLayer()
textLayer.frame = self.bounds
textLayer.string = "Loading..."
textLayer.fontSize = 30
textLayer.font = UIFont.boldSystemFont(ofSize: 30.0)
textLayer.foregroundColor = UIColor.red.cgColor
textLayer.alignmentMode = .center
textLayer.position = CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2)
self.layer.mask = textLayer
这里我们把文字图层设置为了父图层的蒙版属性。这就意味着只有textLayer不透明的部分才会被渲染出来。
4.添加动画
//从左到右
let animation = CABasicAnimation(keyPath: "position.x")
animation.fromValue = 0
animation.toValue = self.bounds.size.width
animation.duration = 2
animation.repeatCount = Float.greatestFiniteMagnitude
backLayer.add(animation, forKey: nil)
为普通的背景图层添加动画,从左向右移动,这样就可以出现上面的效果咯。
结语
通过本文,我们深入了解了 CAReplicatorLayer 的基本用法和一些进阶技巧。从创建简单的加载动画到实现围绕方框移动、绘制 logo 和闪烁文字等复杂效果,CAReplicatorLayer 展现了其强大的功能和灵活性。
CAReplicatorLayer 不仅能够大幅提高动画的性能,还能让开发者用简洁的代码实现复杂的视觉效果。希望本文能为大家提供一些灵感,鼓励大家在实际开发中多多尝试,利用 CAReplicatorLayer 创造出更多精彩的动画效果。