lopes710
lopes710

Reputation: 415

SwiftUI chart animation

I want to do a chart animation of where 2 paths present each other from left to right. I have the following code:

GeometryReader { geometry in
            let height = geometry.size.height
            let width = geometry.size.width

            Path { path in
                path.move(to: CGPoint(x: 0, y: -height * self.ratio(for: 0)))

                for index in 1..<dataPoints.count {
                    path.addLine(to: CGPoint(
                        x: CGFloat(index) * width / CGFloat(dataPoints.count - 1),
                        y: -height * self.ratio(for: index)))
                }
            }
            .trim(from: 0, to: end)
            .stroke(Color.accentColor, style: StrokeStyle(lineWidth: 5, lineJoin: .round))
            .animation(.linear(duration: 2))
            .onAppear(perform: {
                end = 1
            })
        }

enter image description here

The animation is trimming from left to right which looks good. Problem is the starting point of Path. The chart moves from "behind the scenes" top-left position. I don't know how to set this before the animation starts. If I create a var path: Path how do I initialise with, e.g., CGRect if this is inside a subview and I just the width and height inside the GeometryReader ViewBuilder? Please help. Thank you in advance.

Edit: I was suspicious on the timing of rendering the screen of the .onAppear closure. I got it working using DispatchQueue. Solution:

.onAppear {
    DispatchQueue.main.async {
        end = 1
    }
}

Upvotes: 0

Views: 2011

Answers (1)

ChrisR
ChrisR

Reputation: 12125

The problem lies in this line:
-height * self.ratio(for: index)))

I don't know what you self.ratio(for:) func does, and I don't understand why you use -height ... this seems to lead to the line starting at the upper left corner of your view. As points are added the view grows larger into negative values and gets pushed down.

You should instead start at y: height (of GeometryReader) and grow smaller but not negative.

enter image description here

struct ContentView: View {
    
    @State private var end = 0.0
    
    let dataPoints: [Double] = [3, 4, 7, 17, 11, 15, 4]
    
    var body: some View {
        VStack {
            GeometryReader { geometry in
                let height = geometry.size.height
                let width = geometry.size.width
                
                let maxValue = dataPoints.max(by: <) ?? 1
                
                Path { path in
                    path.move(to: CGPoint(
                        x: 0,
                        y: height - dataPoints[0] / maxValue * height ) )
                    
                    for index in 1..<dataPoints.count {
                        path.addLine(to: CGPoint(
                            x: CGFloat(index) * width / CGFloat(dataPoints.count - 1),
                            y: height - dataPoints[index] / maxValue * height ))
                    }
                }
                .trim(from: 0, to: end)
                .stroke(Color.accentColor, style: StrokeStyle(lineWidth: 5, lineJoin: .round))
                .animation(.linear(duration: 2), value: end)
            }
            
            Button("Start") { end = 1 }
        }
        .frame(height: 300)
    }
}

Upvotes: 1

Related Questions