Swift Charts Slow Navigation

I'm experiencing a problem where my NavigationLink hangs when on the same view as a somewhat complicated chart.

Additional Context:

My Swift Chart has a fair number of data points (2000+). I'm also doing custom modification using the .chartYAxis modifier.

.chartYAxis {
    AxisMarks(values: .stride(by: 1)) { value in
           AxisGridLine()
           AxisTick()
           // In my app I also put a value label here, but that doesn't seem to be the thing in particular that slows this down.
     }
}

When I do this modifier along with having a big set of data points, if I have a NavigationLink elsewhere on the same view, it hangs when I press the NavigationLink. Notably, if I remove this modifier, it doesn't hang. (By hang I mean it takes about 1s to navigate to the next view).

Here's a slightly contrived example that mirrors my actual app, minus the generation of data points:

import SwiftUI
import Charts

struct ContentView: View {
    var body: some View {
        NavigationStack {
            NavigationLink(value: "NavigationKey") {
                Text("Link")
            }

            ChartView()
            Text("Main View")
                .navigationDestination(for: String.self) { path in
                    SomeView()
                }
        }
    }
}

struct ChartView: View {
    var chartXY: [(x: Double, y: Double)] {
        var entries: [(x: Double, y: Double)] = []
        for i in 0..<2500 
            let y = Double.random(in: 0..<8000)
            entries.append((x: Double(i), y: y))
        }

        return entries
    }

    

    var body: some View {
        List {
            Chart {
                ForEach(chartXY, id: \.x) {
                    LineMark(x: .value("x", $0.x), y: .value("y", $0.y))
                }
            }
            .frame(minHeight: 100.0)

            // Commenting out this block == no hang
            .chartYAxis {
                AxisMarks(values: .stride(by: 1)) { value in
                    AxisGridLine()
                    AxisTick()

                    // In my app I also put a value label here, but that doesn't seem to be the thing in particular that slows this down.
                }
            }
        }
    }

}



struct SomeView: View {
    var body: some View {
        Text("hello")
    }
}

Presumably, given commenting out the block solves the problem, SwiftUI is preparing some large amounts of AxisMarks/GridLines/AxisTicks for the view transition. Is there someway for me to indicate that it shouldn't do that to prevent this problem? Or is there some other reason this would be hanging?

Post not yet marked as solved Up vote post of joeypriest Down vote post of joeypriest
3.1k views

Replies

Did some profiling of my real app instead of this contrived example, and while these modifiers are a factor, there's something else related to my chart slowing down the navigation transition.

Digging into TimeProfiler shows AttributeGraph being the slowest.

Will followup if I figure out what the problem is.

I'm working with a slightly different architecture now, and I'm still seeing performance problems.

In summary, I have:

  • A NavigationStack
  • A list
  • 7 charts in that list

Each chart has a variable amount of data points, but on average about 2000. If I tap a navigation destination, the app hangs for 1-2s. I've noticed a correlation, where if I whittle down my data points on each chart, the app hang shortens as well. Even down to less than 100 data points, my hang is more than 0.5s, so there's still clearly something wrong or inefficient going on.

Still not confident if it's my code that's inefficient or if there's a problem with Charts or NavigationStack, but I figured I'd journal my latest findings.

Edit: Actually after both reducing samples, and removing my chart modifiers, my hang is almost not noticeable. So I think it's a combination of both samples + the modifiers I'm using on the charts.

So somewhere with that is the problem....but why does it slow down navigation so much?

hi @joeypries

Thanks for showing updates / progress, one little question, on real device, in xcode what is your CPU usage? For me, whatever I do, it goes to the 100% to render a chart ... And I can't find a workaround to reduce it ...

Thanks

Hey, I am experiencing the same problem and found an ugly workaround that "fixed" the lag.

I added a @State showGraph that is false by default, when the view appears it is set to true and false when the view disappears. In the body I added if showGraph around my graph.

This fixes the lag but the Graph won't be shown for the first split second, it is still much faster than the ~600 ms hang I was experiencing. To me it seems there is a problem with Graphs and Navigation Stack.

Cheers

@State showGraph solution is not working for me. Also I cannot reduce the datapoints in chart.

Is there any other solution for this?

@State showGraph isn't working for me either. I'm also looking for another solution for this.

Same issue, distilled it down to a very simple example with around 100 hardcoded datapoints, and transitions in and out of the view containing the chart in a navigation stack has noticeable lag. Its a really simple chart, it shouldn't have any lag at all.

Another +1 on this here. I'm trying to draw Charts with ~1,000 point marks and scroll through them in a page style tab view. Even on the latest devices there's noticeable lag. Was wondering if there's anything that can be done to pre-draw them, optimise the data set or something. Currently I just use:

In the View initialiser:

let data:[(Date,Double)]

And in the view body:

ForEach(0..<data.count, id: \.self) { index in
    PointMark(
        x: .value("Time", data[index].0),
        y: .value("Data", data[index].1)
            )
}

Many thanks for any suggestions

I think the issue is not with Charts but with the page style tab view. Have you tried with a ScrollView set to .scrollTargetBehavior(.paging)? If you can target iOS 17 you'll see a massive performance improvement.