4

I have created a line graph of a fixed width that takes in "data" (an array of doubles), using this tutorial. Here is my code:

struct ChartView: View {
    
    var data: [Double]
    private let maxY: Double = Double(Constants.maxRange)
    private let minY: Double = 0
    
    var body: some View {
        
        VStack (spacing: 0) {
            HStack (spacing: 0) {
                chartYAxis
                    .frame(width: 40)
                Divider()
                chartView
                    .background(chartBackground)
                Divider()
            }
            HStack (spacing: 0) {
                Rectangle()
                    .fill(Color.clear)
                    .frame(width: 40, height: 20)
                chartXAxis
            }
        }
        .frame(height: 300)
        .font(.caption)
        .foregroundColor(Color("TextColor"))
    }
}

extension ChartView {
    
    private var chartView: some View {
        GeometryReader { geometry in
            Path { path in
                for index in data.indices {
                    
                    let xPosition = geometry.size.width / CGFloat(data.count - 1) * CGFloat(index)
                    
                    let yAxis = maxY - minY
                    let yPosition = (1 - CGFloat((data[index] - minY) / yAxis)) * geometry.size.height
                    
                    if (index == 0) {
                        path.move(to: CGPoint(x: xPosition, y: yPosition))
                    }
                    path.addLine(to: CGPoint(x: xPosition, y: yPosition ))
                }
            }
            .stroke(Color.blue, style: StrokeStyle(lineWidth: 3, lineCap: .round, lineJoin: .round))
        }
    }
    
    private var chartBackground: some View {
        VStack {
            Divider()
            Spacer()
            Divider()
            Spacer()
            Divider()
        }
    }
    
    private var chartYAxis: some View {
        VStack {
            Text("\(maxY, specifier: "%.0f")")
            Spacer()
            let midY = (maxY + minY) / 2
            Text("\(midY, specifier: "%.0f")")
            Spacer()
            Text("\(minY, specifier: "%.0f")")
        }
    }
    
    private var chartXAxis: some View {
        HStack {
            ForEach(0..<data.count, id: \.self) { index in
                Text("\(index + 1)")
                if (index < (data.count - 1)) {
                    Spacer()
                }
            }
        }
    }
}

The array "data" will append values as per button press by the user, and at any time should have around 2-20 values.

Every time value is appended to "data", I want to animate this change. The animation should not redraw the line from left to right (like here) but squeeze the existing line together to make room for the appended value. Here is a very janky example of how I want the graph to work. However, I would like the changes to be smoother, similar to this example.

I want the graph frame to remain the same size with appended values to "data", not to scroll to add new values as in this question.

I have looked at some examples of animatable vectors in line graphs, but none address this problem I am having.

Does anyone know if there is any software or method I could use to achieve this? I am fairly new to Swift, so I am hoping this is possible.

Felix H.
  • 75
  • 1
  • 6

0 Answers0