Blog

SwiftUI Path Animations

In the fifth of our Thinking in SwiftUI challenges, we asked you to animate a path.

We started the challenge with code that draws a line. The starting point changes based on a boolean property, but despite the withAnimation, the path itself doesn't animate:

								let p1 = CGPoint(x: 50, y: 50)
let p2 = CGPoint(x: 100, y: 25)
let p3 = CGPoint(x: 100, y: 100)

struct ContentView: View {
    @State var toggle = true
    var body: some View {
        VStack {
            Button("Toggle") {
                withAnimation { self.toggle.toggle() }
            }
            Path { p in
                p.move(to: toggle ? p1 : p2)
                p.addLine(to: p3)
            }.stroke(lineWidth: 2)          
        }
    }
}

							

To animate a path, we need to tell SwiftUI which properties are animatable. One way to do this is by wrapping the path in a custom Shape. We create a Line shape with properties for both the line's start and end points:

								struct Line: Shape {
    var start, end: CGPoint

    func path(in rect: CGRect) -> Path {
        Path { p in
            p.move(to: start)
            p.addLine(to: end)
        }
    }
}

							

To animate the start and end properties, we need to expose them via animatableData, and the type of animatableData needs to conform to the VectorArithmetic protocol. Unfortunately, CGPoint does not conform to VectorArithmetic, and it's bad practice to add this conformance ourselves: you're not supposed to conform types you don't own to protocols you don't own, even though in this case, there wouldn't be much harm in it.

Luckily, CGPoint does conform to Animatable, so we can use its animatableData. We can now make both points of the Line animatable by creating an animatable pair out of the two CGPoint.Animatable values:

								extension Line {
    var animatableData: AnimatablePair<CGPoint.AnimatableData, CGPoint.AnimatableData> {
        get { AnimatablePair(start.animatableData, end.animatableData) }
        set { (start.animatableData, end.animatableData) = (newValue.first, newValue.second) }
    }
}

							

Our new book, Thinking in SwiftUI, discusses the animation system in more detail in chapter six. You can join the early access here.


Stay up-to-date with our newsletter or follow us on Twitter .

Back to the Blog

Recent Posts