Blog

SwiftUI Line Graph Animation

In the sixth of our Thinking in SwiftUI challenges, we asked you to animate a line graph.

As a first step, we’ll create a shape for the line graph. This shape takes an array of data points (normalized to 0...1). Any points that are outside this range are drawn outside of the proposed rectangle rect. For clarity, we pulled out the function that computes a CGPoint for a given data point.

struct LineGraph: Shape {
    var dataPoints: [CGFloat]

    func path(in rect: CGRect) -> Path {
        func point(at ix: Int) -> CGPoint {
            let point = dataPoints[ix]
            let x = rect.width * CGFloat(ix) / CGFloat(dataPoints.count - 1)
            let y = (1-point) * rect.height
            return CGPoint(x: x, y: y)
        }

        return Path { p in
            guard dataPoints.count > 1 else { return }
            let start = dataPoints[0]
            p.move(to: CGPoint(x: 0, y: (1-start) * rect.height))
            for idx in dataPoints.indices {
                p.addLine(to: point(at: idx))
            }
        }
    }
}

SwiftUI has built-in support for trimming shapes using the .trim modifier: this trims the path of the shape to the specified start and end points. We keep a constant starting point, 0, but vary the end point during the animation (from 0 to 1). Since the trim modifier has built-in support for animating the start and end points, we can animate the graph simply by animating between a to value of 0 and 1. Because the graph is defined as a Shape, we can use the built in modifiers to style it: stroke it with a red color, fix it to an aspect ratio, add a border, and apply some padding:

struct ContentView: View {
    @State var on = true

    var body: some View {
        VStack {
            LineGraph(dataPoints: sampleData)
                .trim(to: on ? 1 : 0)
                .stroke(Color.red, lineWidth: 2)
                .aspectRatio(16/9, contentMode: .fit)
                .border(Color.gray, width: 1)
                .padding()
            Button("Animate") {
                withAnimation(.easeInOut(duration: 2)) {
                    self.on.toggle()
                }
            }
        }
    }
}

That’s all we need for our line graph β€”Β or any shape, really β€” to animate:

Our new book, Thinking in SwiftUI, discusses the animation system in more detail in chapter six. You can buy it here.


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

Back to the Blog

recent posts