swift-charts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Swift Charts

Swift Charts

Build data visualizations with Swift Charts targeting iOS 26+. Compose marks inside a
Chart
container, configure axes and scales with view modifiers, and use vectorized plots for large datasets.
See
references/charts-patterns.md
for extended patterns, accessibility, and theming guidance.
使用面向iOS 26+的Swift Charts构建数据可视化。在
Chart
容器中组合标记,通过视图修饰符配置坐标轴和刻度,针对大数据集使用矢量化绘图能力。
查看
references/charts-patterns.md
获取扩展模式、无障碍适配和主题设计指引。

Workflow

工作流程

1. Build a new chart

1. 构建新图表

  1. Define data as an
    Identifiable
    struct or use
    id:
    key path.
  2. Choose mark type(s):
    BarMark
    ,
    LineMark
    ,
    PointMark
    ,
    AreaMark
    ,
    RuleMark
    ,
    RectangleMark
    , or
    SectorMark
    .
  3. Wrap marks in a
    Chart
    container.
  4. Encode visual channels:
    .foregroundStyle(by:)
    ,
    .symbol(by:)
    ,
    .lineStyle(by:)
    .
  5. Configure axes with
    .chartXAxis
    /
    .chartYAxis
    .
  6. Set scale domains with
    .chartXScale(domain:)
    /
    .chartYScale(domain:)
    .
  7. Add selection, scrolling, or annotations as needed.
  8. For 1000+ data points, use vectorized plots (
    BarPlot
    ,
    LinePlot
    , etc.).
  1. 将数据定义为
    Identifiable
    结构体或使用
    id:
    键路径。
  2. 选择标记类型:
    BarMark
    LineMark
    PointMark
    AreaMark
    RuleMark
    RectangleMark
    SectorMark
  3. 将标记包裹在
    Chart
    容器中。
  4. 配置视觉通道编码:
    .foregroundStyle(by:)
    .symbol(by:)
    .lineStyle(by:)
  5. 使用
    .chartXAxis
    /
    .chartYAxis
    配置坐标轴。
  6. 使用
    .chartXScale(domain:)
    /
    .chartYScale(domain:)
    设置刻度域。
  7. 根据需要添加选择、滚动或注释功能。
  8. 数据点超过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 ordering
swift
.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
    Identifiable
    or chart uses
    id:
    key path
  • Model uses
    @Observable
    with
    @State
    , not
    ObservableObject
  • Mark type matches goal (bar=comparison, line=trend, sector=proportion)
  • Multi-series lines use
    series:
    parameter or
    .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 (
    Date?
    for date axis)
  • Scrollable charts set
    .chartXVisibleDomain(length:)
    for viewport
  • 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
    搭配
    @State
    ,而非
    ObservableObject
  • 标记类型符合使用目标(柱状图=对比、折线图=趋势、扇形图=占比)
  • 多系列折线使用
    series:
    参数或
    .foregroundStyle(by:)
    配置
  • 坐标轴配置了合适的标签、刻度和网格线
  • 零点基线重要的场景显式设置了刻度域
  • 饼图/环形图限制在5-7个切片,小数值合并为「其他」
  • 选择绑定类型与坐标轴数据类型匹配(日期轴对应
    Date?
  • 可滚动图表设置了
    .chartXVisibleDomain(length:)
    控制视口
  • 超过1000个数据点的数据集使用矢量化绘图
  • 矢量化绘图中KeyPath修饰符放在值修饰符之前
  • 为标记添加了无障碍标签支持VoiceOver
  • 图表经过Dynamic Type和暗黑模式测试
  • 图例可见且位置合理,或已明确隐藏
  • 图表数据模型类型符合Sendable要求,在@MainActor中更新图表数据

API Verification

API核验

Use the apple-docs MCP
fetchAppleDocumentation
with paths like
/documentation/Charts/BarMark
,
/documentation/Charts/Chart
, or
/documentation/Charts/LinePlot
to verify API details.
使用apple-docs MCP的
fetchAppleDocumentation
接口,传入
/documentation/Charts/BarMark
/documentation/Charts/Chart
/documentation/Charts/LinePlot
等路径核验API详情。

References

参考资料