Performance degradation and redraw loops when syncing SwiftUI Charts with custom AxisMarks

I am reporting a reproducible performance issue in iOS 18.6 where synchronizing the scroll position of two Chart views via chartScrollPosition(id:) causes a complete redraw loop when custom AxisMarks are used. This occurs even when the axis marks are technically "hidden," leading to significant frame drops and stuttering on modern hardware like the iPhone 15.

Environment

Device: iPhone 15 OS: iOS 18.6 (22G86) Frameworks: SwiftUI, Swift Charts, Observation

The Issue When using a shared @Observable state to sync two charts, the scrolling is fluid only if the axes are at their default settings. As soon as a custom AxisMarks block is added to either chart, the following behavior is observed:

Diffing Failure: The framework appears unable to maintain the identity of the axis components during the scroll update.

Redraw Loop: Instead of an incremental scroll translation, the diffing algorithm triggers a full reload/re-render of both charts on every scroll offset change.

Impact: CPU spikes to 100% and the UI becomes unresponsive.

This happens even if the custom AxisMarks is used solely to hide the axis (e.g., AxisMarks { _ in }), suggesting the issue is with the custom declaration itself rather than the complexity of the marks being rendered.

Steps to Reproduce Create two Chart views in a VStack. Bind both to a single @Observable property using .chartScrollPosition(id: $state.pos). Add any .chartXAxis { AxisMarks(...) { ... } } modifier. Scroll either chart; observe the stuttering.

import SwiftUI import Charts import Observation

@Observable class ChartState { var scrollPos: Date = .now }

struct PerformanceBugView: View { @State private var state = ChartState()

var body: some View {
    VStack {
        Chart(data) { ... }
            .chartScrollPosition(id: $state.scrollPos)
            .chartXAxis {
                // This custom mark triggers the performance issue
                AxisMarks { _ in AxisValueLabel() }
            }

        Chart(data) { ... }
            .chartScrollPosition(id: $state.scrollPos)
    }
}

}

Questions for the Community/Apple Engineers: Is there a way to provide a stable identifier to AxisMarks to prevent them from being treated as "new" during a scroll update? Why does even an empty AxisMarks block (used for hiding) trigger a layout invalidation that standard axes do not? Are there internal optimizations for chartScrollPosition that are bypassed when the axis layout is customized?

Performance degradation and redraw loops when syncing SwiftUI Charts with custom AxisMarks
 
 
Q