2

I'm trying to animate a line graph using SwiftUI, Path and Shape. I can't get each path.addLine to appear individually as each line is added

This is what I've tried



import SwiftUI

struct LineChartShape: Shape{
    
    var chartItems: [ChartItem]

    var animatableData: [ChartItem] {
        get { chartItems }
        set {
            self.chartItems = newValue
        }
    }

    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let width = rect.width
        let height = rect.height
        path.move(to: CGPoint(x: width * 0.05, y: height * 0.5))
        
        chartItems.forEach { chartItem in
            path.addLine(to: CGPoint(x: width * CGFloat(chartItem.x), y:  height * CGFloat(chartItem.y)))
        }
        
        return path
    }
    
    
}

struct LineChartShape_Previews: PreviewProvider {
    static var previews: some View {
        GeometryReader { geometry in
            
            LineChartShape(chartItems: [
                ChartItem(y: 0.5, x: 0.05),
                ChartItem(y: 0.4, x: 0.1),
                ChartItem(y: 0.2, x: 0.15),
                ChartItem(y: 0.3, x: 0.2),
                ChartItem(y: 0.3, x: 0.25),
                ChartItem(y: 0.4, x: 0.3),
                ChartItem(y: 0.5, x: 0.35),
                ChartItem(y: 0.3, x: 0.4),
                ChartItem(y: 0.6, x: 0.45),
                ChartItem(y: 0.65, x: 0.5),
                ChartItem(y: 0.5, x: 0.55),
                ChartItem(y: 0.5, x: 0.6),
                ChartItem(y: 0.4, x: 0.65),
                ChartItem(y: 0.45, x: 0.7),
                ChartItem(y: 0.3, x: 0.75),
                ChartItem(y: 0.3, x: 0.8),
                ChartItem(y: 0.2, x: 0.85),
                ChartItem(y: 0.3, x: 0.9)
            ])
                .stroke(Color.blue, lineWidth: 5)
                .animation(.easeInOut(duration: 10))
                

        }
    }
}

struct ChartItem: Identifiable{
    let id = UUID()
    var y: Float
    var x: Float
    
}

I'm new to Swift so I'm sure I'm missing something obvious but I can't figure it out. Thanks for your help

bluesman
  • 2,242
  • 2
  • 25
  • 35

2 Answers2

8

Here is a demo of possible solution. Tested with Xcode 12.

demo

struct TestAnimateAddShape: View {
    @State private var end = CGFloat.zero
    var body: some View {
        GeometryReader { geometry in

            LineChartShape(chartItems: [
                ChartItem(y: 0.5, x: 0.05),
                ChartItem(y: 0.4, x: 0.1),
                ChartItem(y: 0.2, x: 0.15),
                ChartItem(y: 0.3, x: 0.2),
                ChartItem(y: 0.3, x: 0.25),
                ChartItem(y: 0.4, x: 0.3),
                ChartItem(y: 0.5, x: 0.35),
                ChartItem(y: 0.3, x: 0.4),
                ChartItem(y: 0.6, x: 0.45),
                ChartItem(y: 0.65, x: 0.5),
                ChartItem(y: 0.5, x: 0.55),
                ChartItem(y: 0.5, x: 0.6),
                ChartItem(y: 0.4, x: 0.65),
                ChartItem(y: 0.45, x: 0.7),
                ChartItem(y: 0.3, x: 0.75),
                ChartItem(y: 0.3, x: 0.8),
                ChartItem(y: 0.2, x: 0.85),
                ChartItem(y: 0.3, x: 0.9)
            ])
                .trim(from: 0, to: end)            // << here !!
                .stroke(Color.blue, lineWidth: 5)
                .animation(.easeInOut(duration: 10))
        }.onAppear { self.end = 1 }                  // << activate !!
    }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
0

Great solution! I recommend however to remove the animation modifier and use the WithAnimation function instead:

   withAnimation(.easeIn(duration: x) {
     self.end = 1 
   }

The reason is that if you change the screen orientation, I found that the line moves weirdly over the screen... This can be prevented, if you only start the animation from onAppear.

domi852
  • 497
  • 1
  • 4
  • 13