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