如何在SwiftUI中实现路径动画

8

由于对SwiftUI不熟悉,而且这个新框架的文档还不太多,我想知道有没有人了解如何在SwiftUI中对路径进行动画。

例如,给定一个视图,比如这个简单的RingView:

struct RingView : View {   
    var body: some View {
        GeometryReader { geometry in
            Group {
                // create outer ring path
                Path { path in
                    path.addArc(center: center,
                                radius: outerRadius,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 360),
                                clockwise: true)
                }
                .stroke(Color.blue)

                // create inner ring
                Path { path in
                    path.addArc(center: center,
                                radius: outerRadius,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 180),
                                clockwise: true)
                }
                .stroke(Color.red)
                .animation(.basic(duration: 2, curve: .linear))
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }
}

所显示的是:

enter image description here

现在我想知道如何对内环进行动画处理,也就是蓝线内的红色线条。我想要实现的动画是简单的从起点开始绘制路径到终点的动画。
使用CoreGraphics和旧的UIKit框架相当简单,但是似乎将一个简单的.animation(.basic(duration: 2, curve: .linear))添加到内路径并在withAnimation块中显示视图并没有任何效果。
我已经查看了SwiftUI提供的Apple教程,但它们只涵盖更深入的视图(例如Image)上的移动/缩放动画。
有关如何在SwiftUI中对PathShape进行动画处理的任何指导?
1个回答

30

在WWDC 237(使用SwiftUI构建自定义视图)中展示了路径动画的示例。关键是使用AnimatableData。您可以跳到31:23开始观看,但我建议您至少从27:47开始。

您还需要下载示例代码,因为有趣的部分(也没有解释)在演示中没有显示出来:https://developer.apple.com/documentation/swiftui/drawing_and_animation/building_custom_views_in_swiftui


更多文档: 自从我最初发布答案以来,我继续研究如何对路径执行动画,并发布了一篇详细解释Animatable协议以及如何在路径中使用它的文章:https://swiftui-lab.com/swiftui-animations-part1/


更新:

我一直在使用形状路径动画。这是一个GIF。

enter image description here

这是代码:

重要提示:该代码无法在Xcode Live预览中执行动画。它需要在模拟器或实际设备上运行。

import SwiftUI

struct ContentView : View {
    var body: some View {
        RingSpinner().padding(20)
    }
}

struct RingSpinner : View {
    @State var pct: Double = 0.0

    var animation: Animation {
        Animation.basic(duration: 1.5).repeatForever(autoreverses: false)
    }

    var body: some View {

        GeometryReader { geometry in
            ZStack {
                Path { path in

                    path.addArc(center: CGPoint(x: geometry.size.width/2, y: geometry.size.width/2),
                                radius: geometry.size.width/2,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 360),
                                clockwise: true)
                }
                .stroke(Color.green, lineWidth: 40)

                InnerRing(pct: self.pct).stroke(Color.yellow, lineWidth: 20)
            }
        }
        .aspectRatio(1, contentMode: .fit)
            .padding(20)
            .onAppear() {
                withAnimation(self.animation) {
                    self.pct = 1.0
                }
        }
    }

}

struct InnerRing : Shape {
    var lagAmmount = 0.35
    var pct: Double

    func path(in rect: CGRect) -> Path {

        let end = pct * 360
        var start: Double

        if pct > (1 - lagAmmount) {
            start = 360 * (2 * pct - 1.0)
        } else if pct > lagAmmount {
            start = 360 * (pct - lagAmmount)
        } else {
            start = 0
        }

        var p = Path()

        p.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/2),
                 radius: rect.size.width/2,
                 startAngle: Angle(degrees: start),
                 endAngle: Angle(degrees: end),
                 clockwise: false)

        return p
    }

    var animatableData: Double {
        get { return pct }
        set { pct = newValue }
    }
}

我更新了我的答案,包括一个可工作的环形动画示例。 - kontiki
我又进行了一次更新。现在不再需要使用DispatchQueue来创建多个动画,只需要使用一个即可。 - kontiki
我将代码复制到项目的新文件中,我可以看到绿色的圆圈但没有动画(与您发布的GIF不同)。有什么我遗漏的吗?谢谢! - Rony Rozen
2
我在学习SwiftUI的第二天就停止使用实时预览了。我无法相信它,最终只是浪费了时间。我想我会等到GM版本发布再试一次。 - kontiki
//类型“Animation”没有成员“basic” Animation.basic(duration: 1.5).repeatForever(autoreverses: false) //您可以替换为以下内容 Animation.easeIn(duration: 1.5).repeatForever(autoreverses: false) - SHARON D ROSE BE
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接