在 UIView 中的 UIBezierPath 中对 startAngle 进行动画处理

发布于 2025-01-19 10:22:21 字数 1515 浏览 3 评论 0原文

我有一个 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 帖子中提取的,但它工作正常,我正在使用它在界面中绘制静态弧。

我的问题是,如何对弧线进行动画处理,特别是 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()
    }

非常感谢收到的任何帮助。

I have a UIView subclass, which draws an arc inside its frame. The draw method looks like this:

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
    }

This code was pulled from another SO'flow post, but it works fine and I'm using it to draw static arcs in an interface.

My question is, how can I animate the arcs, and specifically the startAngle of the UIBezierPath? I gather so far that this cannot be done with a regular UIView animation, so CABasicAnimation seems to be the go-to. But I can't figure out how to specify the property to be animated in a block of code like this:

    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()
    }

Any help gratefully received.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

×眷恋的温暖 2025-01-26 10:22:21

您可以使用基于图层的方法来绘制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)

然后,这个Sublayer的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")

Rather than drawing the path in draw, you can use a layer-based approach. Add a CAShapeLayer as a sublayer of your view.

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)

Then this sublayer's strokeStart can be animated, which is kind of like animating the start angle. The value of strokeStart and strokeEnd ranges from 0 to 1, and they basically mean "which part of the line do you want to be stroked". Now you can convert the "from" and "to" values 0 and 40 to values for strokeStart.

The layer can then be animated like this:

// 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")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文