swift - CABasicAnimation 开始时的故障
问题描述
我有一个缩放/脉冲动画,它在 for 循环中处理 cgPaths,如下所示。此代码有效,但仅当您已将附加animatedLayers
到circleLayer
路径时才有效,并且这会在动画 ( ) 开始之前创建一个静态圆圈(类似故障)...circleLayer
subLayer
DispatchQueue
...
self.layer.addSublayer(animatedLayer)
animatedLayers.append(animatedLayer)
...
是否可以将CAShapeLayer
带参数添加到子层?如果没有,有什么推荐的替代品吗?
import UIKit
import Foundation
@IBDesignable
class AnimatedCircleView: UIView {
// MARK: - Initializers
var animatedLayers = [CAShapeLayer]()
// MARK: - Methods
override func draw(_ rect: CGRect) {
// Animated circle
for _ in 0...3 {
let animatedPath = UIBezierPath(arcCenter: .zero, radius: self.layer.bounds.size.width / 2.3,
startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
let animatedLayer = CAShapeLayer()
animatedLayer.path = animatedPath.cgPath
animatedLayer.strokeColor = UIColor.black.cgColor
animatedLayer.lineWidth = 0
animatedLayer.fillColor = UIColor.gray.cgColor
animatedLayer.lineCap = CAShapeLayerLineCap.round
animatedLayer.position = CGPoint(x: self.layer.bounds.size.width / 2, y: self.layer.bounds.size.width / 2)
self.layer.addSublayer(animatedLayer)
animatedLayers.append(animatedLayer)
}
// Dispatch animation for circle _ 0...3
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.animateCircle(index: 0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.animateCircle(index: 1)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.animateCircle(index: 2)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
self.animateCircle(index: 3)
}
}
}
}
}
func animateCircle(index: Int) {
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.duration = 1.8
scaleAnimation.fromValue = 0
scaleAnimation.toValue = 1
scaleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
scaleAnimation.repeatCount = Float.infinity
animatedLayers[index].add(scaleAnimation, forKey: "scale")
let opacityAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
opacityAnimation.duration = 1.8
opacityAnimation.fromValue = 0.7
opacityAnimation.toValue = 0
opacityAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
opacityAnimation.repeatCount = Float.infinity
animatedLayers[index].add(opacityAnimation, forKey: "opacity")
}
}
解决方案
关键问题是您的动画开始时有交错的延迟,在 0.1 到 1.0 秒之间。在最后一个动画开始之前,该层只是坐在那里,全尺寸和 100% 不透明度。
由于您正在将变换比例从 0 设置为 1,因此我建议将起始变换设置为 0(或将 更改opacity
为 0)。然后你不会看到他们坐在那里,直到他们各自的动画开始。
其他一些观察:
这
draw(_:)
不是添加图层、启动动画等的正确位置。此方法可能会被多次调用,并且应该代表给定时间点的视图。我会退休draw(_:)
,因为它不是开始这个的正确地方,你根本不需要这种方法。您在 0.1 秒后开始您的第一个动画。为什么不立即启动呢?
path
您应该通过将和position
属性的设置推迟到 来处理框架调整layoutSubviews
。
因此:
@IBDesignable
class AnimatedCircleView: UIView {
private var animatedLayers = [CAShapeLayer]()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(arcCenter: .zero, radius: bounds.width / 2.3, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
for animatedLayer in animatedLayers {
animatedLayer.path = path.cgPath
animatedLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
}
}
}
// MARK: - Methods
private extension AnimatedCircleView {
func configure() {
for _ in 0...3 {
let animatedLayer = CAShapeLayer()
animatedLayer.strokeColor = UIColor.black.cgColor
animatedLayer.lineWidth = 0
animatedLayer.fillColor = UIColor.gray.cgColor
animatedLayer.lineCap = .round
animatedLayer.transform = CATransform3DMakeScale(0, 0, 1)
layer.addSublayer(animatedLayer)
animatedLayers.append(animatedLayer)
}
self.animateCircle(index: 0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.animateCircle(index: 1)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.animateCircle(index: 2)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.animateCircle(index: 3)
}
}
}
}
func animateCircle(index: Int) {
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.duration = 1.8
scaleAnimation.fromValue = 0
scaleAnimation.toValue = 1
scaleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
scaleAnimation.repeatCount = .greatestFiniteMagnitude
animatedLayers[index].add(scaleAnimation, forKey: "scale")
let opacityAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
opacityAnimation.duration = 1.8
opacityAnimation.fromValue = 0.7
opacityAnimation.toValue = 0
opacityAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
opacityAnimation.repeatCount = .greatestFiniteMagnitude
animatedLayers[index].add(opacityAnimation, forKey: "opacity")
}
}
推荐阅读
- debian - 'override_dh_auto_configure' 失败,因为 gcc 的参数/标志无法识别
- python - 基本 Flask 应用程序未运行(TypeError:模块中缺少必填字段“type_ignores”)
- postgresql - 使用 postgresql 在第一个斜杠后拆分字符串
- android - 如何在 PlaceHolderFragment 的 onPause 方法中使用 findViewById 找到我的文本视图
- javascript - WordPress 自定义小部件图像选择器 - 禁用保存按钮
- c++ - 将一系列整数放入一个类的最佳方法是什么?
- c++ - 如何在硬币找零问题中找到硬币的数量
- nativescript - 错误:android.view.InflateException 使用 nativescript 插件反馈
- r - 映射并更改数据框中的列名
- java - 您可以直接从 InputStream 创建 FileWriter 或 BufferedWriter 吗?