SwiftUI Line Graph Animation
In the sixth of our Thinking in SwiftUI challenges, we asked you to animate a line graph.
SwiftUI Challenge #6: Line Graph Animations π
— objc.io (@objcio) March 11, 2020
Implement and animate a line graph. For a bonus challenge: add a small dot that follows the tip of the graph.
Reply with your solution, and we'll post ours next Tuesday. π pic.twitter.com/M6HJVN3lE9
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.