Hello SwiftUI learners,
In this post you will learn how to create a custom line chart animation like in the video below. This will be done by using the shape protocol. Is it called protocol? I’m not sure but it doesn’t matter. We will also go crazy with dispatchqueue as you can see in the code block.
So copy the code and go crazy, just FYI. This is a prototype for now but I plan to use this type of animation for a onboarding flow in my next app. If you have any questions, please feel free to comment.
struct PerformanceExampleView: View {
@State private var drawLine1: CGFloat = 0.0
@State private var drawLine2: CGFloat = 0.0
@State private var drawLine3: CGFloat = 0.0
@State private var drawLine4: CGFloat = 0.0
@State private var drawLine5: CGFloat = 0.0
@State private var drawLine6: CGFloat = 0.0
@State private var drawLine7: CGFloat = 0.0
var body: some View {
VStack {
VStack {
ChartLineAnimation(
drawLine1: drawLine1,
drawLine2: drawLine2,
drawLine3: drawLine3,
drawLine4: drawLine4,
drawLine5: drawLine5,
drawLine6: drawLine6,
drawLine7: drawLine7
)
.stroke(style: StrokeStyle(lineWidth: 3))
}
.frame(width: 280, height: 260)
.padding()
.padding(.horizontal, 10)
.background(
Color.green
.cornerRadius(15)
.offset(y: -40)
)
Button(action: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine1 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine2 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine3 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine4 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine5 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine6 = 40.0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
withAnimation(.easeInOut(duration: 0.2)) {
drawLine7 = 40.0
}
}
}
}
}
}
}
}
}, label: {
Text("Animate")
})
}
}
}
struct ChartLineAnimation: Shape {
var drawLine1: CGFloat
var drawLine2: CGFloat
var drawLine3: CGFloat
var drawLine4: CGFloat
var drawLine5: CGFloat
var drawLine6: CGFloat
var drawLine7: CGFloat
var animatableData: AnimatablePair<AnimatablePair<AnimatablePair<AnimatablePair<AnimatablePair<AnimatablePair<CGFloat, CGFloat>, CGFloat>, CGFloat>, CGFloat>, CGFloat>, CGFloat> {
get {
AnimatablePair(AnimatablePair(AnimatablePair(AnimatablePair(AnimatablePair(AnimatablePair(drawLine1, drawLine2), drawLine3),drawLine4),drawLine5),drawLine6),drawLine7)
}
set {
drawLine1 = newValue.first.first.first.first.first.first
drawLine2 = newValue.first.first.first.first.first.second
drawLine3 = newValue.first.first.first.first.second
drawLine4 = newValue.first.first.first.second
drawLine5 = newValue.first.first.second
drawLine6 = newValue.first.second
drawLine7 = newValue.second
}
}
func path(in rect: CGRect) -> Path {
Path { path in
// Start point
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
// First segment: Move 40 to the right and 25 up
let endX1 = rect.minX + drawLine1
let endY1 = rect.midY - (drawLine1 * 25 / 40)
path.addLine(to: CGPoint(x: endX1, y: endY1))
// Second segment: Continue 40 to the right and 60 down
let endX2 = endX1 + drawLine2
let endY2 = endY1 + (drawLine2 * 60 / 40)
if drawLine2 > 0 {
path.addLine(to: CGPoint(x: endX2, y: endY2))
}
// Third segment: Continue 40 to the right and 100 up
let endX3 = endX2 + drawLine3
let endY3 = endY2 - (drawLine3 * 100 / 40)
if drawLine3 > 0 {
path.addLine(to: CGPoint(x: endX3, y: endY3))
}
// Fourth segment: Continue 40 to the right and 40 down
let endX4 = endX3 + drawLine4
let endY4 = endY3 + (drawLine4 * 40 / 40)
if drawLine4 > 0 {
path.addLine(to: CGPoint(x: endX4, y: endY4))
}
// Fifth segment: Continue 40 to the right and 75 up
let endX5 = endX4 + drawLine5
let endY5 = endY4 - (drawLine5 * 75 / 40)
if drawLine5 > 0 {
path.addLine(to: CGPoint(x: endX5, y: endY5))
}
// Sixth segment: Continue 40 to the right and 50 down
let endX6 = endX5 + drawLine6
let endY6 = endY5 + (drawLine6 * 50 / 40)
if drawLine6 > 0 {
path.addLine(to: CGPoint(x: endX6, y: endY6))
}
// Seventh segment: Continue 40 to the right and 80 up
let endX7 = endX6 + drawLine7
let endY7 = endY6 - (drawLine7 * 80 / 40)
if drawLine7 > 0 {
path.addLine(to: CGPoint(x: endX7, y: endY7))
}
}
}
}
/Mr SwiftUI