案例:多数据集的组合图表
概述
本章案例将演示如何使用 SwiftUI Charts 框架创建包含多个数据集的组合图表。我们将构建一个同时展示销售额和利润趋势的混合图表,结合折线图和柱状图来呈现不同维度的数据。
实现步骤
1. 准备数据模型
struct SalesData: Identifiable {
let id = UUID()
let month: String
let revenue: Double
let profit: Double
}
let sampleData: [SalesData] = [
SalesData(month: "Jan", revenue: 15000, profit: 5000),
SalesData(month: "Feb", revenue: 22000, profit: 7500),
// ...其他月份数据
]
2. 创建基础图表框架
Chart {
// 柱状图表示销售额
ForEach(sampleData) { data in
BarMark(
x: .value("Month", data.month),
y: .value("Revenue", data.revenue)
)
.foregroundStyle(.blue)
}
// 折线图表示利润
ForEach(sampleData) { data in
LineMark(
x: .value("Month", data.month),
y: .value("Profit", data.profit)
)
.foregroundStyle(.green)
.lineStyle(StrokeStyle(lineWidth: 3))
}
}
.chartYAxis {
// 主Y轴(左侧)
AxisMarks(position: .leading)
// 次Y轴(右侧)
AxisMarks(position: .trailing)
}
3. 添加图表修饰符
.chartForegroundStyleScale([
"Revenue": .blue,
"Profit": .green
])
.chartLegend(position: .top)
.chartXAxis {
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.month(.abbreviated))
}
}
4. 实现交互功能
@State private var selectedMonth: String?
Chart {
// ...之前的图表内容
// 添加交互标记
if let selectedMonth {
RuleMark(
x: .value("Selected", selectedMonth)
)
.foregroundStyle(.gray.opacity(0.3))
.annotation(position: .top) {
// 显示详细数据的注解
}
}
}
.chartOverlay { proxy in
GeometryReader { geometry in
Rectangle().fill(.clear).contentShape(Rectangle())
.gesture(
DragGesture()
.onChanged { value in
let xPosition = value.location.x
guard let month: String = proxy.value(atX: xPosition) else { return }
selectedMonth = month
}
)
}
}
关键技术与注意事项
双Y轴处理:
- 使用不同度量单位时,需要合理设置轴的位置和比例
- 可以通过
.chartYScale(domain: 0...maxValue)调整范围
数据对齐:
- 确保不同图表类型使用相同的x轴值
- 对时间序列数据使用
Date类型而非字符串
视觉区分:
- 为不同数据集使用对比明显的颜色
- 添加图例说明各数据系列含义
性能优化:
- 大数据集时考虑使用
.chartXScale和.chartYScale限制显示范围 - 对静态数据使用
let声明而非@State
- 大数据集时考虑使用
完整示例扩展
添加平均值参考线
let avgRevenue = sampleData.map { $0.revenue }.reduce(0, +) / Double(sampleData.count)
Chart {
// ...现有图表内容
RuleMark(
y: .value("Average", avgRevenue)
)
.foregroundStyle(.blue)
.lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
}
响应式设计处理
@Environment(\.horizontalSizeClass) var sizeClass
var body: some View {
chart
.frame(height: sizeClass == .compact ? 300 : 400)
.padding(sizeClass == .compact ? 10 : 20)
}
总结
通过本案例,我们实现了:
- 在同一图表中展示多个数据维度
- 组合使用不同的图表类型(柱状图+折线图)
- 添加交互式元素和注解
- 处理双Y轴和响应式布局
这种组合图表特别适合需要对比分析相关指标的商业应用场景。
