swift-charts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwift Charts
Swift Charts
Build data visualizations with Swift Charts targeting iOS 26+. Compose marks
inside a container, configure axes and scales with view modifiers, and
use vectorized plots for large datasets.
ChartSee for extended patterns, accessibility, and
theming guidance.
references/charts-patterns.md使用面向iOS 26+的Swift Charts构建数据可视化。在容器中组合标记,通过视图修饰符配置坐标轴和刻度,针对大数据集使用矢量化绘图能力。
Chart查看获取扩展模式、无障碍适配和主题设计指引。
references/charts-patterns.mdWorkflow
工作流程
1. Build a new chart
1. 构建新图表
- Define data as an struct or use
Identifiablekey path.id: - Choose mark type(s): ,
BarMark,LineMark,PointMark,AreaMark,RuleMark, orRectangleMark.SectorMark - Wrap marks in a container.
Chart - Encode visual channels: ,
.foregroundStyle(by:),.symbol(by:)..lineStyle(by:) - Configure axes with /
.chartXAxis..chartYAxis - Set scale domains with /
.chartXScale(domain:)..chartYScale(domain:) - Add selection, scrolling, or annotations as needed.
- For 1000+ data points, use vectorized plots (,
BarPlot, etc.).LinePlot
- 将数据定义为结构体或使用
Identifiable键路径。id: - 选择标记类型:、
BarMark、LineMark、PointMark、AreaMark、RuleMark或RectangleMark。SectorMark - 将标记包裹在容器中。
Chart - 配置视觉通道编码:、
.foregroundStyle(by:)、.symbol(by:)。.lineStyle(by:) - 使用/
.chartXAxis配置坐标轴。.chartYAxis - 使用/
.chartXScale(domain:)设置刻度域。.chartYScale(domain:) - 根据需要添加选择、滚动或注释功能。
- 数据点超过1000个时,使用矢量化绘图能力(、
BarPlot等)。LinePlot
2. Review existing chart code
2. 评审现有图表代码
Run through the Review Checklist at the end of this file.
对照本文末尾的评审检查清单逐一核验。
Chart Container
Chart容器
swift
// Data-driven init (single-series)
Chart(sales) { item in
BarMark(x: .value("Month", item.month), y: .value("Revenue", item.revenue))
}
// Content closure init (multi-series, mixed marks)
Chart {
ForEach(seriesA) { item in
LineMark(x: .value("Date", item.date), y: .value("Value", item.value))
.foregroundStyle(.blue)
}
RuleMark(y: .value("Target", 500))
.foregroundStyle(.red)
}
// Custom ID key path
Chart(data, id: \.category) { item in
BarMark(x: .value("Category", item.category), y: .value("Count", item.count))
}swift
// 数据驱动初始化(单系列)
Chart(sales) { item in
BarMark(x: .value("Month", item.month), y: .value("Revenue", item.revenue))
}
// 内容闭包初始化(多系列、混合标记)
Chart {
ForEach(seriesA) { item in
LineMark(x: .value("Date", item.date), y: .value("Value", item.value))
.foregroundStyle(.blue)
}
RuleMark(y: .value("Target", 500))
.foregroundStyle(.red)
}
// 自定义ID键路径
Chart(data, id: \.category) { item in
BarMark(x: .value("Category", item.category), y: .value("Count", item.count))
}Mark Types
标记类型
BarMark (iOS 16+)
BarMark (iOS 16+)
swift
// Vertical bar
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
// Stacked by category (automatic when same x maps to multiple bars)
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.foregroundStyle(by: .value("Product", item.product))
// Horizontal bar
BarMark(x: .value("Sales", item.sales), y: .value("Month", item.month))
// Interval bar (Gantt chart)
BarMark(
xStart: .value("Start", item.start),
xEnd: .value("End", item.end),
y: .value("Task", item.task)
)swift
// 垂直柱状图
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
// 按类别堆叠(同x值对应多个柱时自动生效)
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.foregroundStyle(by: .value("Product", item.product))
// 水平柱状图
BarMark(x: .value("Sales", item.sales), y: .value("Month", item.month))
// 区间柱状图(甘特图)
BarMark(
xStart: .value("Start", item.start),
xEnd: .value("End", item.end),
y: .value("Task", item.task)
)LineMark (iOS 16+)
LineMark (iOS 16+)
swift
// Single line
LineMark(x: .value("Date", item.date), y: .value("Price", item.price))
// Multi-series via foregroundStyle encoding
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
.foregroundStyle(by: .value("City", item.city))
.interpolationMethod(.catmullRom)
// Multi-series with explicit series parameter
LineMark(
x: .value("Date", item.date),
y: .value("Price", item.price),
series: .value("Ticker", item.ticker)
)swift
// 单条折线
LineMark(x: .value("Date", item.date), y: .value("Price", item.price))
// 通过foregroundStyle编码实现多系列
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
.foregroundStyle(by: .value("City", item.city))
.interpolationMethod(.catmullRom)
// 显式指定series参数实现多系列
LineMark(
x: .value("Date", item.date),
y: .value("Price", item.price),
series: .value("Ticker", item.ticker)
)PointMark (iOS 16+)
PointMark (iOS 16+)
swift
PointMark(x: .value("Height", item.height), y: .value("Weight", item.weight))
.foregroundStyle(by: .value("Species", item.species))
.symbol(by: .value("Species", item.species))
.symbolSize(100)swift
PointMark(x: .value("Height", item.height), y: .value("Weight", item.weight))
.foregroundStyle(by: .value("Species", item.species))
.symbol(by: .value("Species", item.species))
.symbolSize(100)AreaMark (iOS 16+)
AreaMark (iOS 16+)
swift
// Stacked area
AreaMark(x: .value("Date", item.date), y: .value("Sales", item.sales))
.foregroundStyle(by: .value("Category", item.category))
// Range band
AreaMark(
x: .value("Date", item.date),
yStart: .value("Min", item.min),
yEnd: .value("Max", item.max)
)
.opacity(0.3)swift
// 堆叠面积图
AreaMark(x: .value("Date", item.date), y: .value("Sales", item.sales))
.foregroundStyle(by: .value("Category", item.category))
// 区间带
AreaMark(
x: .value("Date", item.date),
yStart: .value("Min", item.min),
yEnd: .value("Max", item.max)
)
.opacity(0.3)RuleMark (iOS 16+)
RuleMark (iOS 16+)
swift
RuleMark(y: .value("Target", 9000))
.foregroundStyle(.red)
.lineStyle(StrokeStyle(dash: [5, 3]))
.annotation(position: .top, alignment: .leading) {
Text("Target").font(.caption).foregroundStyle(.red)
}swift
RuleMark(y: .value("Target", 9000))
.foregroundStyle(.red)
.lineStyle(StrokeStyle(dash: [5, 3]))
.annotation(position: .top, alignment: .leading) {
Text("Target").font(.caption).foregroundStyle(.red)
}RectangleMark (iOS 16+)
RectangleMark (iOS 16+)
swift
RectangleMark(x: .value("Hour", item.hour), y: .value("Day", item.day))
.foregroundStyle(by: .value("Intensity", item.intensity))swift
RectangleMark(x: .value("Hour", item.hour), y: .value("Day", item.day))
.foregroundStyle(by: .value("Intensity", item.intensity))SectorMark (iOS 17+)
SectorMark (iOS 17+)
swift
// Pie chart
Chart(data, id: \.name) { item in
SectorMark(angle: .value("Sales", item.sales))
.foregroundStyle(by: .value("Category", item.name))
}
// Donut chart
Chart(data, id: \.name) { item in
SectorMark(
angle: .value("Sales", item.sales),
innerRadius: .ratio(0.618),
outerRadius: .inset(10),
angularInset: 1
)
.cornerRadius(4)
.foregroundStyle(by: .value("Category", item.name))
}swift
// 饼图
Chart(data, id: \.name) { item in
SectorMark(angle: .value("Sales", item.sales))
.foregroundStyle(by: .value("Category", item.name))
}
// 环形图
Chart(data, id: \.name) { item in
SectorMark(
angle: .value("Sales", item.sales),
innerRadius: .ratio(0.618),
outerRadius: .inset(10),
angularInset: 1
)
.cornerRadius(4)
.foregroundStyle(by: .value("Category", item.name))
}Axis Customization
坐标轴自定义
swift
// Hide axes
.chartXAxis(.hidden)
.chartYAxis(.hidden)
// Custom axis content
.chartXAxis {
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.month(.abbreviated))
}
}
// Multiple AxisMarks compositions (different intervals for grid vs. labels)
.chartXAxis {
AxisMarks(values: .stride(by: .day)) { _ in AxisGridLine() }
AxisMarks(values: .stride(by: .week)) { _ in
AxisTick()
AxisValueLabel(format: .dateTime.week())
}
}
// Axis labels (titles)
.chartXAxisLabel("Time", position: .bottom, alignment: .center)
.chartYAxisLabel("Revenue ($)", position: .leading, alignment: .center)swift
// 隐藏坐标轴
.chartXAxis(.hidden)
.chartYAxis(.hidden)
// 自定义坐标轴内容
.chartXAxis {
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.month(.abbreviated))
}
}
// 多AxisMarks组合(网格线和标签使用不同间隔)
.chartXAxis {
AxisMarks(values: .stride(by: .day)) { _ in AxisGridLine() }
AxisMarks(values: .stride(by: .week)) { _ in
AxisTick()
AxisValueLabel(format: .dateTime.week())
}
}
// 坐标轴标题
.chartXAxisLabel("Time", position: .bottom, alignment: .center)
.chartYAxisLabel("Revenue ($)", position: .leading, alignment: .center)Scale Configuration
刻度配置
swift
.chartYScale(domain: 0...100) // Explicit numeric domain
.chartYScale(domain: .automatic(includesZero: true)) // Include zero
.chartYScale(domain: 1...10000, type: .log) // Logarithmic scale
.chartXScale(domain: ["Mon", "Tue", "Wed", "Thu"]) // Categorical orderingswift
.chartYScale(domain: 0...100) // 显式数值域
.chartYScale(domain: .automatic(includesZero: true)) // 包含零点
.chartYScale(domain: 1...10000, type: .log) // 对数刻度
.chartXScale(domain: ["Mon", "Tue", "Wed", "Thu"]) // 分类排序Foreground Style and Encoding
前景样式与编码
swift
BarMark(...).foregroundStyle(.blue) // Static color
BarMark(...).foregroundStyle(by: .value("Category", item.category)) // Data encoding
AreaMark(...).foregroundStyle( // Gradient
.linearGradient(colors: [.blue, .cyan], startPoint: .bottom, endPoint: .top)
)swift
BarMark(...).foregroundStyle(.blue) // 静态颜色
BarMark(...).foregroundStyle(by: .value("Category", item.category)) // 数据驱动编码
AreaMark(...).foregroundStyle( // 渐变
.linearGradient(colors: [.blue, .cyan], startPoint: .bottom, endPoint: .top)
)Selection (iOS 17+)
选择功能 (iOS 17+)
swift
@State private var selectedDate: Date?
@State private var selectedRange: ClosedRange<Date>?
@State private var selectedAngle: String?
// Point selection
Chart(data) { item in
LineMark(x: .value("Date", item.date), y: .value("Value", item.value))
}
.chartXSelection(value: $selectedDate)
// Range selection
.chartXSelection(range: $selectedRange)
// Angular selection (pie/donut)
.chartAngleSelection(value: $selectedAngle)swift
@State private var selectedDate: Date?
@State private var selectedRange: ClosedRange<Date>?
@State private var selectedAngle: String?
// 点选
Chart(data) { item in
LineMark(x: .value("Date", item.date), y: .value("Value", item.value))
}
.chartXSelection(value: $selectedDate)
// 区间选择
.chartXSelection(range: $selectedRange)
// 环形角度选择(饼图/环形图)
.chartAngleSelection(value: $selectedAngle)Scrollable Charts (iOS 17+)
可滚动图表 (iOS 17+)
swift
Chart(dailyData) { item in
BarMark(x: .value("Date", item.date, unit: .day), y: .value("Steps", item.steps))
}
.chartScrollableAxes(.horizontal)
.chartXVisibleDomain(length: 3600 * 24 * 7) // 7 days visible
.chartScrollPosition(initialX: latestDate)
.chartScrollTargetBehavior(
.valueAligned(matching: DateComponents(hour: 0), majorAlignment: .page)
)swift
Chart(dailyData) { item in
BarMark(x: .value("Date", item.date, unit: .day), y: .value("Steps", item.steps))
}
.chartScrollableAxes(.horizontal)
.chartXVisibleDomain(length: 3600 * 24 * 7) // 可视区域为7天
.chartScrollPosition(initialX: latestDate)
.chartScrollTargetBehavior(
.valueAligned(matching: DateComponents(hour: 0), majorAlignment: .page)
)Annotations
注释
swift
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.annotation(position: .top, alignment: .center, spacing: 4) {
Text("\(item.sales, format: .number)").font(.caption2)
}
// Overflow resolution
.annotation(
position: .top,
overflowResolution: .init(x: .fit(to: .chart), y: .padScale)
) { Text("Label") }swift
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.annotation(position: .top, alignment: .center, spacing: 4) {
Text("\(item.sales, format: .number)").font(.caption2)
}
// 溢出处理
.annotation(
position: .top,
overflowResolution: .init(x: .fit(to: .chart), y: .padScale)
) { Text("Label") }Legend
图例
swift
.chartLegend(.hidden) // Hide
.chartLegend(position: .bottom, alignment: .center, spacing: 10) // Position
.chartLegend(position: .bottom) { // Custom
HStack(spacing: 16) {
ForEach(categories, id: \.self) { cat in
Label(cat, systemImage: "circle.fill").font(.caption)
}
}
}swift
.chartLegend(.hidden) // 隐藏图例
.chartLegend(position: .bottom, alignment: .center, spacing: 10) // 调整位置
.chartLegend(position: .bottom) { // 自定义图例
HStack(spacing: 16) {
ForEach(categories, id: \.self) { cat in
Label(cat, systemImage: "circle.fill").font(.caption)
}
}
}Vectorized Plots (iOS 18+)
矢量化绘图 (iOS 18+)
Use for large datasets (1000+ points). Accept entire collections or functions.
swift
// Data-driven
Chart {
BarPlot(sales, x: .value("Month", \.month), y: .value("Revenue", \.revenue))
.foregroundStyle(\.barColor)
}
// Function plotting: y = f(x)
Chart {
LinePlot(x: "x", y: "y", domain: -5...5) { x in sin(x) }
}
// Parametric: (x, y) = f(t)
Chart {
LinePlot(x: "x", y: "y", t: "t", domain: 0...(2 * .pi)) { t in
(x: cos(t), y: sin(t))
}
}Apply KeyPath-based modifiers before simple-value modifiers:
swift
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.foregroundStyle(\.color) // KeyPath first
.opacity(0.8) // Value modifier second适用于大数据集(1000+数据点)场景,支持传入完整集合或函数。
swift
// 数据驱动
Chart {
BarPlot(sales, x: .value("Month", \.month), y: .value("Revenue", \.revenue))
.foregroundStyle(\.barColor)
}
// 函数绘制:y = f(x)
Chart {
LinePlot(x: "x", y: "y", domain: -5...5) { x in sin(x) }
}
// 参数方程:(x, y) = f(t)
Chart {
LinePlot(x: "x", y: "y", t: "t", domain: 0...(2 * .pi)) { t in
(x: cos(t), y: sin(t))
}
}基于KeyPath的修饰符要放在简单值修饰符之前:
swift
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.foregroundStyle(\.color) // 先写KeyPath修饰符
.opacity(0.8) // 再写值修饰符Common Mistakes
常见错误
1. Using ObservableObject instead of @Observable
1. 使用ObservableObject代替@Observable
swift
// WRONG
class ChartModel: ObservableObject {
@Published var data: [Sale] = []
}
struct ChartView: View {
@StateObject private var model = ChartModel()
}
// CORRECT
@Observable class ChartModel {
var data: [Sale] = []
}
struct ChartView: View {
@State private var model = ChartModel()
}swift
// 错误
class ChartModel: ObservableObject {
@Published var data: [Sale] = []
}
struct ChartView: View {
@StateObject private var model = ChartModel()
}
// 正确
@Observable class ChartModel {
var data: [Sale] = []
}
struct ChartView: View {
@State private var model = ChartModel()
}2. Missing series parameter for multi-line charts
2. 多折线图缺少series参数
swift
// WRONG -- all points connect into one line
Chart {
ForEach(allCities) { item in
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
}
}
// CORRECT -- separate lines per city
Chart {
ForEach(allCities) { item in
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
.foregroundStyle(by: .value("City", item.city))
}
}swift
// 错误 -- 所有点会连接成一条线
Chart {
ForEach(allCities) { item in
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
}
}
// 正确 -- 按城市拆分独立折线
Chart {
ForEach(allCities) { item in
LineMark(x: .value("Date", item.date), y: .value("Temp", item.temp))
.foregroundStyle(by: .value("City", item.city))
}
}3. Too many SectorMark slices
3. SectorMark切片过多
swift
// WRONG -- 20 tiny sectors are unreadable
Chart(twentyCategories, id: \.name) { item in
SectorMark(angle: .value("Value", item.value))
}
// CORRECT -- group into top 5 + "Other"
Chart(groupedData, id: \.name) { item in
SectorMark(angle: .value("Value", item.value))
.foregroundStyle(by: .value("Category", item.name))
}swift
// 错误 -- 20个极小的切片可读性极差
Chart(twentyCategories, id: \.name) { item in
SectorMark(angle: .value("Value", item.value))
}
// 正确 -- 合并为前5名 + "其他"
Chart(groupedData, id: \.name) { item in
SectorMark(angle: .value("Value", item.value))
.foregroundStyle(by: .value("Category", item.name))
}4. Missing scale domain when zero-baseline matters
4. 零点基线重要的场景缺少刻度域配置
swift
// WRONG -- axis starts at ~95; small changes look dramatic
Chart(data) {
LineMark(x: .value("Day", $0.day), y: .value("Score", $0.score))
}
// CORRECT -- explicit domain for honest representation
Chart(data) {
LineMark(x: .value("Day", $0.day), y: .value("Score", $0.score))
}
.chartYScale(domain: 0...100)swift
// 错误 -- 坐标轴从~95开始,小幅变化会被放大
Chart(data) {
LineMark(x: .value("Day", $0.day), y: .value("Score", $0.score))
}
// 正确 -- 显式指定刻度域保证数据展示客观
Chart(data) {
LineMark(x: .value("Day", $0.day), y: .value("Score", $0.score))
}
.chartYScale(domain: 0...100)5. Static foregroundStyle overriding data encoding
5. 静态foregroundStyle覆盖数据编码
swift
// WRONG -- static color overrides by-value encoding
BarMark(x: .value("X", item.x), y: .value("Y", item.y))
.foregroundStyle(by: .value("Category", item.category))
.foregroundStyle(.blue)
// CORRECT -- use only the data encoding
BarMark(x: .value("X", item.x), y: .value("Y", item.y))
.foregroundStyle(by: .value("Category", item.category))swift
// 错误 -- 静态颜色会覆盖按值编码的样式
BarMark(x: .value("X", item.x), y: .value("Y", item.y))
.foregroundStyle(by: .value("Category", item.category))
.foregroundStyle(.blue)
// 正确 -- 仅保留数据编码配置
BarMark(x: .value("X", item.x), y: .value("Y", item.y))
.foregroundStyle(by: .value("Category", item.category))6. Individual marks for 10,000+ data points
6. 10000+数据点使用独立标记
swift
// WRONG -- creates 10,000 mark views; slow
Chart(largeDataset) { item in
PointMark(x: .value("X", item.x), y: .value("Y", item.y))
}
// CORRECT -- vectorized plot (iOS 18+)
Chart {
PointPlot(largeDataset, x: .value("X", \.x), y: .value("Y", \.y))
}swift
// 错误 -- 创建10000个标记视图,性能极差
Chart(largeDataset) { item in
PointMark(x: .value("X", item.x), y: .value("Y", item.y))
}
// 正确 -- 使用矢量化绘图(iOS 18+)
Chart {
PointPlot(largeDataset, x: .value("X", \.x), y: .value("Y", \.y))
}7. Fixed chart height breaking Dynamic Type
7. 固定图表高度破坏Dynamic Type适配
swift
// WRONG -- clips axis labels at large text sizes
Chart(data) { ... }
.frame(height: 200)
// CORRECT -- adaptive sizing
Chart(data) { ... }
.frame(minHeight: 200, maxHeight: 400)swift
// 错误 -- 大字体场景下坐标轴标签会被裁剪
Chart(data) { ... }
.frame(height: 200)
// 正确 -- 自适应尺寸
Chart(data) { ... }
.frame(minHeight: 200, maxHeight: 400)8. KeyPath modifier after value modifier on vectorized plots
8. 矢量化绘图中KeyPath修饰符放在值修饰符之后
swift
// WRONG -- compiler error
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.opacity(0.8)
.foregroundStyle(\.color)
// CORRECT -- KeyPath modifiers first
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.foregroundStyle(\.color)
.opacity(0.8)swift
// 错误 -- 编译报错
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.opacity(0.8)
.foregroundStyle(\.color)
// 正确 -- KeyPath修饰符放在前面
BarPlot(data, x: .value("X", \.x), y: .value("Y", \.y))
.foregroundStyle(\.color)
.opacity(0.8)9. Missing accessibility labels
9. 缺少无障碍标签
swift
// WRONG -- VoiceOver users get no context
Chart(data) {
BarMark(x: .value("Month", $0.month), y: .value("Sales", $0.sales))
}
// CORRECT -- add per-mark accessibility
Chart(data) { item in
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.accessibilityLabel("\(item.month)")
.accessibilityValue("\(item.sales) units sold")
}swift
// 错误 -- VoiceOver用户无法获取上下文信息
Chart(data) {
BarMark(x: .value("Month", $0.month), y: .value("Sales", $0.sales))
}
// 正确 -- 为每个标记添加无障碍信息
Chart(data) { item in
BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
.accessibilityLabel("\(item.month)")
.accessibilityValue("\(item.sales) units sold")
}Review Checklist
评审检查清单
- Data model uses or chart uses
Identifiablekey pathid: - Model uses with
@Observable, not@StateObservableObject - Mark type matches goal (bar=comparison, line=trend, sector=proportion)
- Multi-series lines use parameter or
series:.foregroundStyle(by:) - Axes configured with appropriate labels, ticks, and grid lines
- Scale domain set explicitly when zero-baseline matters
- Pie/donut limited to 5-7 sectors; small values grouped into "Other"
- Selection binding type matches axis data type (for date axis)
Date? - Scrollable charts set for viewport
.chartXVisibleDomain(length:) - Vectorized plots used for datasets exceeding 1000 points
- KeyPath modifiers applied before value modifiers on vectorized plots
- Accessibility labels added to marks for VoiceOver
- Chart tested with Dynamic Type and Dark Mode
- Legend visible and positioned, or intentionally hidden
- Ensure chart data model types are Sendable; update chart data on @MainActor
- 数据模型实现或图表使用
Identifiable键路径id: - 模型使用搭配
@Observable,而非@StateObservableObject - 标记类型符合使用目标(柱状图=对比、折线图=趋势、扇形图=占比)
- 多系列折线使用参数或
series:配置.foregroundStyle(by:) - 坐标轴配置了合适的标签、刻度和网格线
- 零点基线重要的场景显式设置了刻度域
- 饼图/环形图限制在5-7个切片,小数值合并为「其他」
- 选择绑定类型与坐标轴数据类型匹配(日期轴对应)
Date? - 可滚动图表设置了控制视口
.chartXVisibleDomain(length:) - 超过1000个数据点的数据集使用矢量化绘图
- 矢量化绘图中KeyPath修饰符放在值修饰符之前
- 为标记添加了无障碍标签支持VoiceOver
- 图表经过Dynamic Type和暗黑模式测试
- 图例可见且位置合理,或已明确隐藏
- 图表数据模型类型符合Sendable要求,在@MainActor中更新图表数据
API Verification
API核验
Use the apple-docs MCP with paths like
, , or
to verify API details.
fetchAppleDocumentation/documentation/Charts/BarMark/documentation/Charts/Chart/documentation/Charts/LinePlot使用apple-docs MCP的接口,传入、或等路径核验API详情。
fetchAppleDocumentation/documentation/Charts/BarMark/documentation/Charts/Chart/documentation/Charts/LinePlotReferences
参考资料
- Extended patterns:
references/charts-patterns.md - Apple docs: Swift Charts
- Apple docs: Creating a chart using Swift Charts
- 扩展模式:
references/charts-patterns.md - Apple官方文档:Swift Charts
- Apple官方文档:使用Swift Charts创建图表