在 UIView 的 UIBezierPath 中为 startAngle 设置动画

Animate startAngle in UIBezierPath in UIView

我有一个 UIView 子类,它在其框架内绘制了一条弧线。绘制方法如下所示:

override func draw(_ rect: CGRect) {
        let clockwise = true
        let center: CGPoint = CGPoint(x: rect.midX, y: rect.midY)
        let radius: CGFloat = rect.width / 2
        self.trackWidth = min(max(trackWidth, 1), radius)
        arc = UIBezierPath(arcCenter: center,
                                          radius: radius - (trackWidth / 2),
                                          startAngle: startAngle!,
                                          endAngle: endAngle!,
                                          clockwise: clockwise)
        arc!.lineWidth = trackWidth
        arc!.lineCapStyle = .round
        self.color.setStroke()
        arc!.stroke()

        return
    }

此代码是从另一个 SO'flow post 中提取的,但它工作正常,我正在使用它在界面中绘制静态弧。

我的问题是,如何为弧线制作动画,特别是 UIBezierPath 的 startAngle?到目前为止,我收集到这不能用常规的 UIView 动画来完成,所以 CABasicAnimation 似乎是首选。但是我不知道如何在这样的代码块中指定要设置动画的 属性:

    func animateArc() {
        CATransaction.begin()
        var arc = arcs.first!
        DemoView.animate(withDuration: 2.0) {
            arc.layoutIfNeeded()
        }
        let animation = CABasicAnimation(keyPath: "startAngle")
        animation.fromValue = 0
        animation.toValue = DemoView.radianizer(40)
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        
        arc.layer.add(animation, forKey: "startAngle")
        CATransaction.commit()
    }

感谢收到的任何帮助。

您可以使用 layer-based 方法,而不是在 draw 中绘制路径。添加 CAShapeLayer 作为视图的子层。

let arcLayer = CAShapeLayer()
arcLayer.path = yourFullArc
arcLayer.strokeColor = self.color.cgColor
arcLayer.fillColor = UIColor.clear.cgColor
arcLayer.lineWidth = trackWidth
arcLayer.lineCap = .round
self.layer.addSublayer(arcLayer)

然后这个子层的 strokeStart 可以被动画化,这有点像动画化开始角度。 strokeStartstrokeEnd 的取值范围从 0 到 1,它们的基本意思是“你想抚摸线条的哪一部分”。现在您可以将“从”和“到”值 0 和 40 转换为 strokeStart.

的值

然后可以像这样对图层进行动画处理:

// calculating values for strokeStart
let angleRange = endAngle! - startAngle!
let from = startAngle! / angleRange
let to = (startAngle! + DemoView.radianizer(40)) / angleRange

let animation = CABasicAnimation(keyPath: "strokeStart")
animation.fromValue = from
animation.toValue = to
animation.autoreverses = false
animation.isRemovedOnCompletion = false
arcLayer.add(animation, forKey: "someAnimation")