SwiftUI Charts问题:X轴数据点默认间距不均如何解决?
解决Swift Charts中X轴日期点间距不均的问题
问题描述
我正在为应用实现一款追踪游戏利润变化的折线图,用于展示年度利润进度。目前使用模拟数据(涵盖2023年1月、2月、3月、5月、12月的日期)调试图表样式,但因X轴数据点间距不均,导致图表显示异常。
原始代码:
let dummyData: [dummySession] = [ .init(date: Date.from(year: 2023, month: 1, day: 1), profit: 0), .init(date: Date.from(year: 2023, month: 2, day: 12), profit: 55), .init(date: Date.from(year: 2023, month: 3, day: 20), profit: 376), .init(date: Date.from(year: 2023, month: 5, day: 2), profit: 120), .init(date: Date.from(year: 2023, month: 5, day: 18), profit: 500), .init(date: Date.from(year: 2023, month: 12, day: 11), profit: 222) ] VStack { Chart { ForEach(dummyData) { session in LineMark(x: .value("Date", session.date, unit: .month), y: .value("Profit", session.profit)) .lineStyle(.init(lineWidth: 3, lineCap: .round, lineJoin: .round)) } .interpolationMethod(.cardinal) } .chartXAxis { AxisMarks(stroke: StrokeStyle(lineWidth: 0)) } .chartYAxis { AxisMarks(position: .trailing, values: .automatic) { value in AxisGridLine() AxisValueLabel() { if let intValue = value.as(Int.self) { Text(intValue.asCurrency()) .padding(.leading, 25) } } } } } .frame(maxHeight: 300)
图表效果:
问题原因
当前代码中LineMark的x轴指定了unit: .month,Swift Charts会将日期按真实时间跨度排列,导致5月的两个点挤在一起,12月与前一个点间隔7个月,出现间距严重不均的情况。
解决方案
方案1:按数据顺序均匀排列(忽略真实时间间隔)
如果只需要展示数据的先后顺序,不需要体现真实时间跨度,可以将日期转换为分类值(比如字符串),让X轴均匀分配每个数据点的间距:
VStack { Chart { // 遍历数据时格式化日期为短月份标签 ForEach(dummyData) { session in let dateLabel = session.date.formatted(.dateTime.month(.abbreviated)) LineMark(x: .value("Date", dateLabel), y: .value("Profit", session.profit)) .lineStyle(.init(lineWidth: 3, lineCap: .round, lineJoin: .round)) } .interpolationMethod(.cardinal) } .chartXAxis { AxisMarks(stroke: StrokeStyle(lineWidth: 0)) } .chartYAxis { AxisMarks(position: .trailing, values: .automatic) { value in AxisGridLine() AxisValueLabel() { if let intValue = value.as(Int.self) { Text(intValue.asCurrency()) .padding(.leading, 25) } } } } } .frame(maxHeight: 300)
方案2:保留真实时间轴,优化显示范围
如果需要体现真实时间跨度,可通过以下两种方式优化:
子方案A:补充缺失月份的默认数据
生成2023年1-12月的完整数据序列,为缺失月份添加默认利润值(如0),让时间轴连续:
// 生成全年完整数据 var fullYearData: [dummySession] = [] let calendar = Calendar.current for month in 1...12 { // 查找当前月份的已有数据 if let existingSession = dummyData.first(where: { calendar.component(.month, from: $0.date) == month }) { fullYearData.append(existingSession) } else { // 为缺失月份添加默认数据 let defaultDate = Date.from(year: 2023, month: month, day: 1) fullYearData.append(.init(date: defaultDate, profit: 0)) } } // 使用完整数据绘制图表 VStack { Chart { ForEach(fullYearData) { session in LineMark(x: .value("Date", session.date, unit: .month), y: .value("Profit", session.profit)) .lineStyle(.init(lineWidth: 3, lineCap: .round, lineJoin: .round)) } .interpolationMethod(.cardinal) } .chartXAxis { AxisMarks(stroke: StrokeStyle(lineWidth: 0)) } .chartYAxis { AxisMarks(position: .trailing, values: .automatic) { value in AxisGridLine() AxisValueLabel() { if let intValue = value.as(Int.self) { Text(intValue.asCurrency()) .padding(.leading, 25) } } } } } .frame(maxHeight: 300)
子方案B:设置X轴固定时间范围
直接指定X轴的时间范围为2023全年,让图表自动适配完整年度跨度,避免局部间隔过大:
VStack { Chart { ForEach(dummyData) { session in LineMark(x: .value("Date", session.date, unit: .month), y: .value("Profit", session.profit)) .lineStyle(.init(lineWidth: 3, lineCap: .round, lineJoin: .round)) } .interpolationMethod(.cardinal) } .chartXScale(domain: Date.from(year: 2023, month: 1, day: 1)...Date.from(year: 2023, month: 12, day: 31)) .chartXAxis { AxisMarks(stroke: StrokeStyle(lineWidth: 0)) } .chartYAxis { AxisMarks(position: .trailing, values: .automatic) { value in AxisGridLine() AxisValueLabel() { if let intValue = value.as(Int.self) { Text(intValue.asCurrency()) .padding(.leading, 25) } } } } } .frame(maxHeight: 300)
内容的提问来源于stack exchange,提问作者comexbackkid
相关产品推荐
相关产品推荐

