我有一个大约 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)
}
}
您已将
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)
}
}
}
}