SwiftUI 的 Charts 是一个非常强大且直观的框架,专门用于创建数据可视化图表。它引入了简洁的 API 来轻松创建各种类型的图表,比如柱状图、折线图、饼图等。以下是一些关于 SwiftUI Charts 的关键概念和使用方式。
基本结构
在 SwiftUI 中创建图表,需要导入 Charts 模块
import Charts
使用 Chart 视图:
import SwiftUI
import Charts
struct ContentView: View {
var data: [SalesData] = [
SalesData(month: "Jan", sales: 200),
SalesData(month: "Feb", sales: 150),
SalesData(month: "Mar", sales: 300)
]
var body: some View {
Chart(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Sales", item.sales)
)
}
.frame(height: 300)
.padding()
}
}
struct SalesData: Identifiable {
let id = UUID()
let month: String
let sales: Int
}
支持的图表类型
SwiftUI Charts 支持多种类型的图表,每种类型都有其独特的 Mark,用来绘制对应的图表形态。以下是 SwiftUI Charts 目前支持的主要图表类型:
1、柱状图 (Bar Chart)
标记类型: BarMark
用于展示分类数据之间的比较。
既支持垂直柱状图,也支持水平柱状图。
Chart(data) {
BarMark(
x: .value("Category", $0.category),
y: .value("Value", $0.value)
)
}
2、折线图 (Line Chart)
标记类型: LineMark
用于表示连续数据的趋势。
通常用在时间序列数据的可视化中。
Chart(data) {
LineMark(
x: .value("Time", $0.time),
y: .value("Value", $0.value)
)
}
3、面积图 (Area Chart)
标记类型: AreaMark
用于展示数据随时间的变化,并填充曲线下方的区域。
可以叠加多个 AreaMark 创建堆叠图。
Chart(data) {
AreaMark(
x: .value("Date", $0.date),
y: .value("Count", $0.count)
)
}
4、散点图 (Scatter Plot)
标记类型: PointMark
用于表示两个变量之间的关系。
可以通过形状、大小或颜色来区分类别。
Chart(data) {
PointMark(
x: .value("X Value", $0.x),
y: .value("Y Value", $0.y)
)
}
5、饼图 / 环形图 (Pie/Donut Chart)
标记类型: 使用 SectorMark
适用于展示分类数据的占比。
通过调整起点和终点角度绘制。
Chart(data) { item in
SectorMark(
angle: .value("Sales", item.sales)
)
.foregroundStyle(by: .value("Month", item.month))
}
SectorMark默认情况下,它会将所有数据的数值加总,并根据每个数据占总数的比例生成一个完整的圆。数据被映射成扇形角度,但它们没有视觉上的区分,导致它们叠加在一起,看起来像一个单一的圆。
因此,希望饼图能按某个参数分区并以不同颜色显示,可以通过设置 foregroundStyle 来实现。
6、组合图表 (Combination Charts)
组合标记: 使用多种 Mark 叠加。
例如,将柱状图和折线图组合在一起:
Chart(data) {
BarMark(
x: .value("Category", $0.month),
y: .value("Value", $0.sales)
)
LineMark(
x: .value("Category", $0.month),
y: .value("Value", $0.sales)
)
}
7、雷达图 (Radar Chart)
目前 Radar Charts 并未原生支持,需要通过自定义绘制来实现。
8、条形图 (Horizontal Bar Chart)
标记类型: 水平的 BarMark,将 x 和 y 值互换。
Chart(data) {
BarMark(
x: .value("Value", $0.sales),
y: .value("Category", $0.month)
)
}
自定义图表样式
颜色:可以自定义每个 Mark 的颜色。
图例:通过 .chartLegend() 添加图例。
轴样式:自定义轴线、刻度和标签。
BarMark(
x: .value("Category", "Electronics"),
y: .value("Revenue", 1000)
)
.foregroundStyle(Color.blue)
数据动态更新
Charts 的数据源可以绑定到动态数据模型,从而支持实时更新。
@State private var salesData = [
SalesData(month: "Jan", sales: 200),
SalesData(month: "Feb", sales: 150)
]
Button("Add Data") {
salesData.append(SalesData(month: "Mar", sales: 300))
}
Mark参数
在 SwiftUI 的 Charts 中,每个 Mark 都有多个参数,因为它们提供了高度的灵活性和可定制性。这些参数控制图表的外观和行为,使其适配不同的数据结构和可视化需求。
参考示例
struct SampleRating {
let place: String
let rating: Int
static let ratings: [SampleRating] = [
SampleRating(place: "Bellagio", rating: 88),
SampleRating(place: "Paris", rating: 75),
SampleRating(place: "Treasure Island", rating: 33),
SampleRating(place: "Excalibur", rating: 88)
]
}
struct VegasChart: View {
var body: some View {
Chart(SampleRating.ratings, id: \.place) { rating in
SectorMark(angle: .value("Ratings", rating.rating), innerRadius: .ratio(0.5), angularInset: 1)
.cornerRadius(7)
.foregroundStyle(by: .value("Place", rating.rating))
}
.padding()
.frame(height: 500)
}
}
在这段SectorMark的代码中:
1)angle控制扇形的角度大小:.value(“Ratings”, rating.rating) 表示从数据中取 rating.rating 作为角度值,并将该数据绑定到图表的 “Ratings” 维度。
2)innerRadius设置扇形的内半径:.ratio(0.5) 表示内半径是图表外半径的 50%。
3)angularInset设置扇形的间隔角度:angularInset: 1 表示在每个扇形之间留出 1° 的间隙, 提高了可视化的清晰度,特别是在扇形数据较多时。
4)cornerRadius设置扇形的边缘圆角:.cornerRadius(7) 会使扇形的外边缘变得平滑。
5)foregroundStyle设置扇形的不同颜色:.value(“Place”, rating.rating) 将数据绑定到 “Place” 维度, Charts 会自动为不同的 “Place” 分配不同的颜色。通过颜色区分数据类别,提高数据的可读性。
高级功能
组合图表:在一个图表中叠加不同类型的 Mark。
动画效果:通过 withAnimation 添加动态效果。
筛选数据:使用 SwiftUI 的 Filter。
实用场景
数据可视化:展示统计数据(如财务报表、用户增长趋势)。
健康和活动:显示步数、心率等。
科学实验:展示实验结果图表。
总结
SwiftUI Charts 是高度灵活的工具,可以通过以上类型以及自定义样式满足大多数数据可视化需求。
参考文章
iOS 18, SwiftUI 6, & Swift 6: 从零开始构建iOS应用程序, 涵盖visionOS, macOS, watchOS:https://www.bilibili.com/video/BV1b6421f7Px?spm_id_from=333.788.videopod.episodes&vd_source=f21219cb93118beac6a36b0ef961df6a&p=11
扩展知识
Argument ‘x’ must precede argument ‘y’报错
如果遇到以下报错:
Argument 'x' must precede argument 'y'
Replace 'y: .value("Category", $0.month),
x: .value("Value", $0.sales)' with 'x: .value("Value", $0.sales), y: .value("Category", $0.month)'
问题原因为:在 SwiftUI 的 Charts 框架中,参数的顺序有特定要求。BarMark 的参数顺序规定 x 必须在 y 之前。提示 Argument ‘x’ must precede argument ‘y’ 的原因正是因为代码中将 y 参数写在了 x 参数之前,违反了 API 的定义。
解决方案:根据提示修改参数的顺序即可。
BarMark(
x: .value("Value", $0.sales), // x 参数在前
y: .value("Category", $0.month) // y 参数在后
)
设置X轴和Y轴的范围
如果想要调整图表的X轴和Y轴范围,可以通过chartXScale和chartYScale进行调整:
Chart(SampleRating.ratings, id: \.trip) { rating in
PointMark(x: .value("Year", rating.trip), y: .value("Ratings", rating.trip - 2020 + 88))
LineMark(x: .value("Year", rating.trip), y: .value("Ratings", rating.rating))
}
.chartXScale(domain: 2020...2024)
.chartYScale(domain: 1...100)
chartXScale 和 chartYScale 是用于设置图表的 X 轴 和 Y 轴范围的修饰符。它们控制图表显示数据的范围,并帮助调整坐标轴的显示。
chartXScale(domain:):用于设置 X 轴的显示范围。可以定义一个 domain,例如 2020…2025,表示 X 轴的范围从 2020 到 2025。
chartYScale(domain:):用于设置 Y 轴的显示范围。可以定义一个 domain,例如 1…100,表示 Y 轴的范围从 1 到 100。