Hello Swift Charts
Swift & UI 进阶 20m

认识 Swift Charts

Hello Swift Charts

2022年6月6日

在 Apple 官方观看视频

一句话判断

Swift Charts 是今年 SwiftUI 生态中最实用的新增框架——如果你需要在应用里展示数据图表,直接用它就够了。

这场 Session 讲了什么

Swift Charts 是 Apple 全新的声明式图表框架,完全基于 SwiftUI 语法构建。Session 用一个煎饼食品追踪应用的示例,从零开始展示了三种图表的构建过程:

柱状图:展示不同煎饼品种的销量对比。使用 BarMark 配合 ForEach 遍历数据数组。交换 x 和 y 值可以轻松转置为横向柱状图。

多系列柱状图:对比两个城市(Cupertino 和 San Francisco)的每日销量。通过 SwiftUI Picker 切换城市,配合 .animation(.easeInOut) 实现平滑的柱状图过渡动画。

多系列折线图:将两个城市的数据同时展示在一张图中。使用 LineMark 配合 .foregroundStyle(by:) 区分不同系列,PointMark 标注数据点。

Swift Charts 的核心设计理念是”Mark”(标记)——每种图表类型对应一种 Mark(BarMark、LineMark、PointMark 等)。你通过组合不同的 Mark 来构建图表,就像在 SwiftUI 中组合 View 一样。

值得深挖的点

无障碍访问的深度集成是 Swift Charts 的一大亮点。图表数据自动暴露给 VoiceOver,用户可以通过语音导航逐项浏览数据。更酷的是 Audio Graphs——系统可以将图表数据转化为音调变化,用户通过听觉就能感知数据趋势。这两个功能不需要额外代码,Swift Charts 自动处理。

自动适配的轴和标签减少了大量手动调整工作。Swift Charts 根据数据类型自动选择合适的轴样式——日期数据用时间轴,数值数据用线性轴,分类数据用离散轴。深色模式、动态字体、不同设备尺寸都自动适配。

声明式语法的表达力在这里体现得很好。一个 foregroundStyle(by: .value("City", series.city)) 就能自动为不同系列分配颜色和图例。不需要手动管理颜色映射和图例布局。

代码片段

基础柱状图:

struct PancakeSales: Identifiable {
    let name: String
    let sales: Int
    var id: String { name }
}

let data = [
    PancakeSales(name: "Cachapa", sales: 916),
    PancakeSales(name: "Injera", sales: 850),
    PancakeSales(name: "Crêpe", sales: 802),
]

Chart(data) { element in
    BarMark(
        x: .value("销量", element.sales),
        y: .value("品种", element.name)
    )
}
.chartYAxisLabel("煎饼销量")

多系列折线图:

Chart(seriesData) { series in
    ForEach(series.salesData) { data in
        LineMark(
            x: .value("日期", data.date, unit: .day),
            y: .value("销量", data.sales)
        )
        .foregroundStyle(by: .value("城市", series.city))
        
        PointMark(
            x: .value("日期", data.date, unit: .day),
            y: .value("销量", data.sales)
        )
        .foregroundStyle(by: .value("城市", series.city))
    }
}
// 动画过渡
.animation(.easeInOut)

带城市切换的交互式图表:

@State var city: City = .cupertino

Picker("城市", selection: $city) {
    Text("Cupertino").tag(City.cupertino)
    Text("San Francisco").tag(City.sf)
}
.pickerStyle(.segmented)

Chart {
    let data = city == .cupertino ? cupertinoData : sfData
    ForEach(data) { element in
        BarMark(
            x: .value("日期", element.date, unit: .day),
            y: .value("销量", element.sales)
        )
    }
}

最佳实践

  • 数据结构遵循 Identifiable:让 ForEach 和 Chart 初始化器直接接受数据集合
  • .value() 而非直接设值.value("描述", 实际值) 让框架生成正确的轴标签和无障碍描述
  • 横向标签空间不够就转置:交换 x 和 y 即可,不用手动调布局
  • 利用 SwiftUI 动画:数据切换时 .animation() 让图表过渡流畅自然
  • 组合多种 Mark:LineMark + PointMark 是常见搭配,折线加数据点可读性更好

还有什么值得关注

  • Swift Charts 支持 AreaMark(面积图)、RectangleMark(矩形图)等多种 Mark 类型
  • 图表自动支持 VoiceOver 和 Audio Graphs,无需额外代码
  • Dark Mode 和 Dynamic Type 自动适配,Xcode 的 variant 功能可以预览不同外观
  • 框架的设计思路和 SwiftUI 一脉相承,学习曲线非常平缓
WWDC 2022