Swift UI 图表注释很慢

问题描述 投票:0回答:1

我有一个大约 180 点的 SwitUI 图表。我有一个注释作为图表的一部分。注释跟踪鼠标移动的速度很慢,不够快。在下面的代码中我需要做些什么不同的事情来加速对鼠标/手指的注释的跟踪?

它基本上是带有模拟值的折线图和面积图组合。预先感谢

下面是主要的 ContentView 代码,它基本上创建了 LineArea 图表。注释附加到 LineMark

虚拟数据在

.task{ }

中生成
struct ContentView: View {
    @State var selectedDate: Date?
    @State private var isLoading: Bool = true
    @State private var data : [DateValue] = []
    
    var body: some View {
        VStack {
            Chart {
                ForEach(data, id:\.date) { point in
                    LineMark(
                        x: .value("Month", point.date, unit: .month),
                        y: .value("Value", point.value)
                    )
                    .lineStyle(StrokeStyle(lineWidth: 3))
                    if let selectedDate {
                        RuleMark(x: .value("Month", selectedDate.endOfMonth()))
                            .annotation(position: .automatic, alignment: .bottom,
                                        overflowResolution: .init(x: .fit, y: .fit)
                            ) {
                                VStack {
                                    let value = getValue(for: selectedDate.endOfMonth())
                                    Text("Month: \(value.0)")
                                    Text("Value: \(value.1)")
                                }
                                .foregroundColor(.white)
                                .padding()
                                .background {
                                    RoundedRectangle(cornerRadius: 5)
                                }
                            }
                    }
                    AreaMark(
                        x: .value("Month", point.date, unit: .month),
                        y: .value("Value", point.value)
                    )
                }
                .interpolationMethod(.catmullRom)
            }
            .chartXSelection(value: $selectedDate)
            .chartXAxis {
                AxisMarks(values: .automatic) { month in
                    AxisTick()
                    AxisGridLine()
                    AxisValueLabel {
                        if let m = month.as(Date.self) {
                            Text("\(m.toString(format: "MM/yy"))")
                        }
                    }
                }
            }
            .chartYAxis {
                AxisMarks(values: .automatic) { value in
                    AxisTick(centered: false)
                    AxisGridLine()
                    AxisValueLabel {
                        if let v = value.as(Double.self) {
                            Text("\(v/1000.0, specifier: "%0.0fK")")
                        }
                    }
                }
            }
        }
        .task {
            guard isLoading else { return }
            let start = Calendar.current.date(from: DateComponents(year: 2007, month: 1, day: 10))!
            let end = Calendar.current.date(from: DateComponents(year: 2023, month: 12, day: 10))!
            let months = end.months(from: start)

            self.data = (0 ..< months).map{index -> DateValue in
                return DateValue(date: start.plusMonths(index).endOfMonth(), value: Double.random(in: 1000...1600))
            }
            isLoading.toggle()
        }
    }
    
    private func getValue(for date: Date) -> (String, String) {
        let value = data.first(where: {Calendar.current.isDate($0.date, equalTo: date, toGranularity: .month) == true})?.value ?? 0
        return (date.toString(format: "MM/yyyy"), String(format: "%.2f", value))
    }
}

struct DateValue {
    var date: Date
    var value: Double
}

extension Date {
    func startOfMonth() -> Date {
        var components = Calendar.current.dateComponents([.year, .month], from: self)
        components.day = 1
        return Calendar.current.date(from: components)!
    }
    func endOfMonth() -> Date {
        Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth())!
    }
    func plusMonths(_ plus: Int) -> Date {
        Calendar.current.date(byAdding: .month, value: plus, to: self)!
    }
    func months(from date: Date) -> Int {
        Calendar.current.dateComponents([.month], from: date, to:self).month ?? 0
    }
    func toString(format: String = "yyyy-MM-dd") -> String {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.dateFormat = format
        return formatter.string(from: self)
    }
}
swift swiftui swiftui-charts
1个回答
0
投票

您已将

RuleMark
放入
ForEach
内。这意味着当
selectedDate
不为零时,图表上将有大约 180 个
RuleMark
相互重叠。毫不奇怪,这很慢。

您只需要一个

RuleMark
。放在
ForEach
外面。

Chart {
    ForEach(data, id:\.date) { point in
        LineMark(
            x: .value("Month", point.date, unit: .month),
            y: .value("Value", point.value)
        )
        .lineStyle(StrokeStyle(lineWidth: 3))
        AreaMark(
            x: .value("Month", point.date, unit: .month),
            y: .value("Value", point.value)
        )
    }
    .interpolationMethod(.catmullRom)
    
    
    if let selectedDate {
        RuleMark(x: .value("Month", selectedDate.endOfMonth()))
            .annotation(position: .automatic, alignment: .bottom,
                        overflowResolution: .init(x: .fit, y: .fit)
            ) {
                VStack {
                    let value = getValue(for: selectedDate.endOfMonth())
                    Text("Month: \(value.0)")
                    Text("Value: \(value.1)")
                }
                .foregroundColor(.white)
                .padding()
                .background {
                    RoundedRectangle(cornerRadius: 5)
                }
            }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.