Multiple Swift Charts in List diplay inconsistently

I have multiple barmark Charts in a List for SwiftUI. For app running on iOS 17.4.1, and one running on MacOS Sonoma 14.4.1, by scrolling up and down on the list, the order of charts changes. Sometimes one chart will replace another chart, showing duplicate charts in the List. This did not happen for iOS 17.3, and earlier OS versions. Want to see if anyone else face the same issue.

I have checked that correct chart model is used in onAppear, but what is displayed in Chart is not corresponding to the chart model.

Answered by AndyTheBuilder in 785218022
import Charts

struct Value: Identifiable {
    let date: Date
    let val: Double
    let id = UUID()

    init(_ date: Date, _ val: Double) {
        self.date = date
        self.val = val
    }
}

struct Model: Identifiable {
    let name: String
    let color: Color
    let values: [Value]

    var id: String {
        name
    }
}

struct SimpleChartView: View {
    static let day0 = Calendar.current.date(byAdding: DateComponents(day: 0), to: Date())!
    static let day1 = Calendar.current.date(byAdding: DateComponents(day: 1), to: Date())!
    static let day2 = Calendar.current.date(byAdding: DateComponents(day: 2), to: Date())!
    static let day3 = Calendar.current.date(byAdding: DateComponents(day: 3), to: Date())!
    static let day4 = Calendar.current.date(byAdding: DateComponents(day: 4), to: Date())!

    let models: [Model] = [
        .init(name: "Chart 0", color: .yellow, values: [
            Value(Self.day0, 1),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 1", color: .orange, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 1),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 2", color: .pink, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 1),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 3", color: .green, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 1),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 4", color: .blue, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 1)
        ]),
        .init(name: "Chart 5", color: .purple, values: [
            Value(Self.day0, 2),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 6", color: .teal, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 2),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 7", color: .brown, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 2),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 8", color: .gray, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 2),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 9", color: .black, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 2)
        ])
    ]

    var body: some View {
        List {
            ForEach(models) { model in
                Section {
                    VStack(alignment: .leading, spacing: 24) {
                        HStack {
                            Text(model.name)
                            Spacer()
                        }
                        Chart(model.values) { value in
                            BarMark(
                                x: .value("Day", value.date, unit: .day),
                                y: .value("Value", value.val)
                            )
                            .foregroundStyle(model.color)
                        }
                    }
                }
            }
        }
    }
}

I am able to reproduce the issue with the following code. In this example, Chart 0 will have 2.0 height and black color after few scrolls up and down.

Updating from iOS 17.3 to 17.4.1 will trigger the issue to appear.

Do you have any code that we might be able to help you with? Also, I think .onAppear is called when a View is added to the hierarchy, not when it's displayed on-screen.

Accepted Answer
import Charts

struct Value: Identifiable {
    let date: Date
    let val: Double
    let id = UUID()

    init(_ date: Date, _ val: Double) {
        self.date = date
        self.val = val
    }
}

struct Model: Identifiable {
    let name: String
    let color: Color
    let values: [Value]

    var id: String {
        name
    }
}

struct SimpleChartView: View {
    static let day0 = Calendar.current.date(byAdding: DateComponents(day: 0), to: Date())!
    static let day1 = Calendar.current.date(byAdding: DateComponents(day: 1), to: Date())!
    static let day2 = Calendar.current.date(byAdding: DateComponents(day: 2), to: Date())!
    static let day3 = Calendar.current.date(byAdding: DateComponents(day: 3), to: Date())!
    static let day4 = Calendar.current.date(byAdding: DateComponents(day: 4), to: Date())!

    let models: [Model] = [
        .init(name: "Chart 0", color: .yellow, values: [
            Value(Self.day0, 1),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 1", color: .orange, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 1),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 2", color: .pink, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 1),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 3", color: .green, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 1),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 4", color: .blue, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 1)
        ]),
        .init(name: "Chart 5", color: .purple, values: [
            Value(Self.day0, 2),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 6", color: .teal, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 2),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 7", color: .brown, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 2),
            Value(Self.day3, 0),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 8", color: .gray, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 2),
            Value(Self.day4, 0)
        ]),
        .init(name: "Chart 9", color: .black, values: [
            Value(Self.day0, 0),
            Value(Self.day1, 0),
            Value(Self.day2, 0),
            Value(Self.day3, 0),
            Value(Self.day4, 2)
        ])
    ]

    var body: some View {
        List {
            ForEach(models) { model in
                Section {
                    VStack(alignment: .leading, spacing: 24) {
                        HStack {
                            Text(model.name)
                            Spacer()
                        }
                        Chart(model.values) { value in
                            BarMark(
                                x: .value("Day", value.date, unit: .day),
                                y: .value("Value", value.val)
                            )
                            .foregroundStyle(model.color)
                        }
                    }
                }
            }
        }
    }
}

I am able to reproduce the issue with the following code. In this example, Chart 0 will have 2.0 height and black color after few scrolls up and down.

I might possibly have had the same issue as you. I did the following (iOS 17 code) and it solved the issue.

Try putting the "models" inside an

@Observable class <YourModelName>

and then in the view doing

@Bindable var modelBinding = <YourModelName>
ForEach($modelBinding.models) { $value in
    ChartView(value: $value)
}

Multiple Swift Charts in List diplay inconsistently
 
 
Q