大多数浏览器和
Developer App 均支持流媒体播放。
-
Swift Charts:提高标准
深入探索数据可视化:学习 Swift Charts 和 SwiftUI 如何通过各种图表选项帮助您的 App 呈现复杂的数据集。我们将介绍如何基于各种不同的数据进行绘制,并采用标记来创建更加精美的图表。我们还将带您了解 Swift Charts 的大量图表自定义 API,帮助您选择与您的 App 相符的图表样式。为能更好地理解此讲座,我们建议您先观看 WWDC22 的“认识 Swift Charts”。
资源
相关视频
Tech Talks
WWDC22
-
下载
♪ 柔和乐器演奏的嘻哈音乐 ♪ ♪ Donghao Ren:大家好 我是 Donghao 本课将学习更多关于如何 利用 Swift Charts 实现 出色的数据可视化 换句话说 我们要精益求精 让您的 App 用图表可以做更多事 出色的数据可视化 可以让 App 提供更多信息 并且更具吸引力 要在 App 中构建一个好图表 有很多事情需要考虑 当然 我们希望我们的图表忠实地 传达其内部数据并让所有人都能看到 但是 App 中的图表 并非存在于真空中 它们是 App 用户界面的一部分 必须能支持本地化和 OS 功能 如深色模式 图表必须有合适的布局 可以与 UI 的其余部分无缝匹配 图表应该支持动态类型 和设备屏幕尺寸 图表最好还可以在所有平台上运行 并具有出色的动画效果 以获得最佳观感 很多这些基本功能 Swift Charts 都可以 自动解决 让您可以更多地把注意力 放在建立最好的图表上 以传达您独特的数据 让每个人都可以使用 Swift Charts 能实现这些 主要靠 提供一套类似 SwiftUI 的 声明式语法 您只需少量代码 就能在图表中 指定想要的内容 Swift Charts 会 自动生成可以直接使用的出色图表 Swift Charts 还提供 丰富的自定义选项 可以设置图表样式 以匹配您的个性 App 本课的目的是帮助您 对 Swift Charts 有更深入的了解 我们首先介绍声明性语法的 基本部件: 标记和标记的组合 下面举几个 Apple 产品中 使用图表的例子 如您所见 有各种不同的数据 图表类型和样式 Swift Charts 不是为每个图表类型 都提供一个预构建组件 而是按照组合来创建图表 它将少量基本部件 以不同的方式组合 就能让您创建各种各样的图表了 下面讲讲具体如何实现 为此 我要举个例子 我所有的队友都喜欢吃煎饼 所以我们有一个 App 用来跟踪一辆 卖各种煎饼的餐车的订单 这张图表显示了30 天内 派送煎饼的数目 按口味细分 这种图表被称为条形图 在 Swift Charts 中 这样一条蓝色矩形 就是一个标记 标记是表征数据的图形元素 这个条形标记显示最近 30 天内 售出 Cachapa 的数量 这张图表有六个条形标记 每个代表一种煎饼口味 以及相应的销售量 我们看看这样的代码怎么写 这个 SwiftUI 视图 包含一个描述性标题 比如“最畅销口味 Cachapa” 还有一个空图表 这里的图表类型是定义 单个图表的顶层视图 可以像添加其他视图一样 在您的 SwiftUI App 中 添加图表 接下来的课程我们重点讲图表部分 图表中可以添加标记 这里有一个 BarMark 显示 Cachapa 及其销售量 这就是一个单条形标记的图表 截图中可以看到图表跟 用户界面的其余部分匹配 并且有很好的默认样式 比如说 X 轴上四舍五入的整数 如果添加一个不同名称和 销售量的条形标记 就得到了第二个条形图 可以重复此操作以添加更多条形标记 我们可以在一个真实的 App 中 以编程方式生成这些标记 要做到这一点 可以向图表提供一组 结构或元组数组 并使用 ForEach 来为元素的值创建条形标记 如果 ForEach 是图表中 唯一的内容 就像这样 也可以直接将数据放入图表中 许多 SwiftUI 修改器 都可用于标记 比如 可以使用 .foregroundStyle 修改器 设置条形标记的颜色 这里我们将其设置为一个命名颜色 这样可以在 Xcode 中 将其创建为一个命名资源 有一点很重要 图表必须每个人都可访问 默认情况下 图表会对 支持自动生成访问元素的 VoiceOver 用户开放 可以使用 .accessibilityLabel 和 .accessibilityValue 修改器来自定义 比如这里 我们将标签设为 煎饼的名字 值设为销售量 加上“已售”为后缀 于是旁边用户就能获得个性化体验了 旁白:Cachapa 已售 916 个 Injera 已售 850 个 Crêpe 已售 802 个 我们的 App 还可以按天来 跟踪煎饼的售出情况 这个视图显示了过去 30 天内 煎饼的售出情况 该图表详细地提供了 每一天煎饼的售出情况 我们看看制作这个图表的方法 这里 我们有一个 日期和销售额数据的数组 日期由 Date 值表示 从早晨算起 我们将可视化数据用条形图显示 其中 x 显示日期 此处的单位参数中 Date 表示一个日期 所持续的时间 y 显示当天的销售额 如右图所示 这个条形图显示了这些天的销售量 条形图并不是数据可视化的唯一方法 我们再来试试折线图 做折线图唯一不同的操作 就是将 BarMark 换成 LineMark 使用声明式语法可以轻松地 在 Swift Charts 中 切换图表类型 上一张图表显示了 这一段时间内的总销售量 不过这辆餐车在两个城市都有销售点 我们想比较两个城市 每个工作日的销售量 来帮助我们决定去哪里 此图表中的线条 显示了周一至周日的销售量 每条线代表一个城市 我们看看这是如何构建的 假设我们用一个元组数组 定义两个城市的数据 每个城市包含城市名称 和每天的销售量 然后 我们使用 ForEach 在一系列数据中循环 之前的折线图就完成了 为了区分两个城市 我们使用 .foregroundStyle(by:) 修改器 按城市名称设置两行样式 这里 Swift Charts 会自动给这两个城市 选择颜色 并将其添加到线条上 然后添加一个图例 来指示每种颜色的含义 默认颜色是很容易区分的 系统颜色 为了让色盲的人也能读懂图表 我们可以在线条上添加符号 来进一步区分 做法就是添加一个以城市为数据的 .symbol(by:) 修改器 最后 为了让线条看起来更流畅 我们可以使用曲线 作为线条的插值方法 即使做好了两个折线图 我们照样可以重新看回条形图 将标记类型更改为 BarMark 并删除与条形标记无关的修改器 就能得到一个堆叠条形图 这些条形标记会自动堆叠 因为我们现在每个月有两条数据 虽然堆叠条形图非常适合显示 两个城市的总销售量 但用于两个城市 之间的比较就不方便了 为了更方便比较 我们可以把它 转化为带有 .position(by:) 修改器的簇状条形图 到目前为止 我们看了条形标记和线形标记 Swift Charts 也支持其他标记类型 比如点标记 区域标记 规则标记和矩形标记 这些标记还可以相互组合 构建出更复杂的图表 我们看一个例子 我们先看一个显示每月平均 日销售量的折线图 虽然平均值很有用 但我们还想查看最低 和最高的每日销售量 来了解它的极值 我们可以先将这些值添加到数据中 在数据数组中 我们给每个元素 输入每日最大值 和每日最小值 然后可以用区域标记 来将最大值和最小值可视化 其中 x 表示月份 y 则显示每日最小值和最大值 之间的区域 这样 我们就得到了一个图表 以线表示每日均值 以其上下区域表示每日极值 不过 这类数据的可视化方法 不止线加区域这一种 您也可以轻松切换其他标记类型 来探索更多设计选择 例如这里 我们用的就是 BarMark 不过 这里的线似乎 跟条形标记不太匹配 不妨将线条标记改为 RectangleMark 高度设为两个点 这样 矩形标记就在条形标记里 显示为一条短横线 用来表示平均值 您还可以调整这些标记的宽度 比如这里 我们将宽度比例 设置为 0.6 意思是条形和矩形的宽度 是一个月宽度的 60% 如截图所示 最后 我们还想设置一个 显示所有月份平均日销售量的选项 为此 我们首先把前景样式 设置为浅灰色 以淡化前景 然后在 ForEach 之外 添加一个规则标记 其中 y 表示平均值 这样会添加一条横线 为了清楚表明该横线表示年平均值 我们可以用 .annotation 修改器 为规则添加注释 这样会在规则标记的顶部添加一个 顶部对齐的文本标签 我们介绍了几个利用组合 来构建图表的例子 使用和组合这些基本标记 有许多方法 比如箱线图 多系列折线图 人口金字塔 范围图 流图 多系列散点图 热度图 甚至是矢量场图等等 我这里只举了几个例子 实际上用 Swift Charts 构建图表 所能采用的图表种类还有很多 现在 我们继续下面的话题 使用标记属性来绘制数据 Swift Charts 支持三种主要的数据类型 定量数据、定类数据和时间数据 定量数据是一个数值 例如售出的产品数量 房间的温度或股票价格等 Swift Charts 可以把不同 Swift 数值类型 转化为定量数据 如整数和单双精度浮点类型 定类数据或名义数据 表征的是离散的类别或组 例如一个人的名字 一片大陆或一个产品类型 可以使用字符串或 自定义字符串值的枚举 来作为定性数据 时间数据则用于表示时间点或时间段 比如某一天的持续时间 或某笔交易的确切时间等 Swift Charts 的“日期” 为时间数据 图表的工作原理是将抽象数据 比如销售量的值 转化为标记的属性 我们看看 BarMark 它可以用 X Y 值 和前景样式属性来绘制数据 在这个例子中 我们用定量的销售量 作为 x 属性 用定性的名称 作为 y 属性 生成的图表由水平条形图组成 每条的 X 值为销售额 Y 值为名称 如果我们把名称和销售额翻转 让名称作为 X 销售额作为 Y 就得到一个垂直条形图 这里可以看到 BarMark 的行为 取决于 X 和 Y 属性的数据类型 条形图的方向 则取决于定量属性所在的位置 现在 我们再看一个用全部三个属性 来绘制的图表 这里的 X 表示星期几 是时间数据 Y 表示销量 而前景样式表示城市 生成的是一个堆叠条形图 其中 X 轴显示星期几 Y 轴显示销售量 条形颜色显示不同城市 Swift Charts 支持六种标记类型 以及六个标记属性 可以用来绘制数据 因为数据有三种类型 所以会有大量可能的组合 这就是为什么 Swift Charts 可以 只需少量基本部件就能 实现丰富的图表设计 用标记属性来绘制数据时 比如用 Y 表示销售量 Swift Charts 会创建一个映射 将抽象数据转换为 适当的属性值 像这里 它会将销售量的值 转换到屏幕中的 Y 坐标 我们会使用“缩放”一词来表示 像销售量这种抽象数据 到像 Y 位置这种 标记属性的映射 可以将“缩放”视为一个函数 该函数接受数据值并返回一个属性值 例如 这里有一个 yScale 函数 该函数接受销售额 并返回条状标记所在的 Y 之所以命名为“缩放” 是因为位置属性往往需要将输入值 乘以某个因数进行缩放 将其转换为合理的屏幕坐标 使用标记属性绘制数据时 会生成一个缩放比例来将数据 转换为相应的标记属性 例如在这张图表中 我们有三个比例 分别将星期几转换为 X 销售量转换为 Y 城市转换为前景风格 Swift Charts 会自动根据数据 分析所采用的比例 为您创建一个现成的漂亮图表 可以使用缩放修改器 配置图表中的比例 我们看几个例子 在本例中 自动计算出的 Y 轴 比例为 0 到 150 但是 我们想固定 Y 的比例 这样无论当前的销售情况如何 Y 的比例始终可以保持一致 我们可以把它改为 Y 比例 始终为 0 到 200 为此 我们可以使用 .chartYScale 修改器 将比例的范围 设置为 0 到 200 现在可以看到 轴的值域为 0 到 200 了 同样 我们也使用 .chartForegroundStyleScale 修改器 改变这两个城市映射到 前景样式的方式 现在我们两个城市的颜色就变了 现在 我们知道如何组合标记 并使用标记属性绘制数据了 接下来再来深入了解 更多 Swift Charts 提供的 自定义选项吧 图表由轴组成 可能配有图例 还有一个绘制区 轴和图例能帮助我们解读图表 而绘图区域就是两个轴之间的区域 这就是我们用标记来绘制数据的地方 所有这些元素都可以 在 Swift Charts 中自定义 我们先来看几个例子 如何自定义轴和图例 这是一张显示每月总销售量的图表 无需自定义的话 Swift Charts 默认 会将轴的值四舍五入 现在 X 轴给每个季度 都显示一个标签 我们把它改成每个月一个标签吧 并使用一个字母来作为月份标签 首先 我们添加一个 .chartXAxis 修改器 来自定义 X 轴 以 AxisMarks 为内容 不带参数的话 AxisMarks 创建的就是默认轴 我们先从更改轴值开始 由于我们想要固定的日期间隔 因此可以使用 stride(by:) 跟标准库中的 stride 函数类似 现在我们每个月都有一个标签了 但是 默认标签感觉太挤了 从截图中可以看出来 一些标签被截断了 因为空间不够了 我们把标签显示为单个字母吧 为此 可以从单个成分 建立轴标记 包括 AxisGridLine AxisTick 和 AxisValueLabel 将标签的格式设置为使用窄月份名称 现在每个月都显示为一个字母了 结果生成器中收到的参数值 可以提供当前轴值的信息 可以用它来定义 轴标记的位置和风格 例如 这里设置了一个条件 用来测试日期的值是否为 一个季度的第一个月 是的话就可以将其 突出显示为不同的前景风格 如果不是 就只显示一条网格线 没有刻度和标签 由于现在显示的是季度 我们可以将格式更改为季度样式 通过刚刚的自定义 我们得到一个更具个性的 X 轴 用以显示季度数据 其中有次级网格可以显示每个月份 除了值 轴标记还有其他属性 可以用来配置总体外观和样式 假设我们想让 Y 轴 出现在图表的前沿 而不是默认的后沿 可以将位置参数设置为前沿 就能将其移动到前沿了 Swift Charts 会根据 轴和所绘数据的类型 来为轴标记提供默认预设 可以用预设参数来覆盖默认值 例如 这里的 Y 轴就用了 .extended 预设来将 Y 轴 与用户界面的其余部分对齐 某些图表可能不想展示轴 例如 这上面的销售图表 只想显示大致的销售情况 就不需要显示轴了 可以将 .hidden 输入 到图表的轴修改器中 将轴隐藏 图例的配置与轴的配置类似 例如 这张突出显示最佳日期和位置的图表 已经用了不透明度 来突出显示最佳城市了 所以可以隐藏自动生成的图例 为此 可以添加 .chartLegend 修改器 以 .hidden 作为参数 现在再来讲一下绘图区 可以使用 .chartPlotStyle 修改器 来配置图表的绘图区 在尾随闭包中 我们会编写一个函数 输入原始的绘图区 并返回修改后的绘图区 我们看几个例子 有时候 我们可能想让绘图区 有一个精确的尺寸或纵横比 例如这里 我们想要绘图区的高度 由图表中的类别数量来决定 为此 我们可以将 .frame 修改器 应用到绘图区 并返回修改后的绘图区 这样绘图区的高度就设置好了 修改器还能用来实现特殊的视觉效果 例如 在这个暗模式图表中 我们使用了 .background 修改器 来添加粉红色背景 不透明度设为 0.2 以突出显示图表 然后添加一个同样是 粉红色的 1 pt 边框 这就为图表制造了 一种独特的视觉效果 前面我们提到了缩放 即能将数据值映射到 X 和 Y 等标记属性的函数 Swift Charts 提供了一个 ChartProxy 允许读取 图表中的 X 和 Y 比例 可以用 ChartProxy 的 position(for:) 方法 来获取给定数据值的位置 或使用 value(at:) 方法 来获取给定位置的数据值 这样就能将其他视图 与当前图表相匹配 我们看一个例子 我们要构建一个交互式拖动视图 可以在这里通过拖动来选择 图表中某段区间 该区间 将用于过滤详情视图中的行 图表代理对象可以 从 .chartOverlay 或 .chartBackground 修改器中获取 这两个修改器类 似于 SwiftUI 的叠加层 和背景修改器 但可以提供图表代理 要构建这个例子 我们可以像以前一样 先定义基本图表 然后添加一个 .chartOverlay 修改器 修改器可以提供图表代理 代理中有一个几何阅读器 可以读取 图层视图的几何形状 然后 就有了一个 对 SwiftUI 的 DragGesture 有所响应的矩形视图了 拖动时 首先找到 图表绘图区 x 坐标的起点和当前位置 其范围就是手势提供的位置 减去绘图区的起点 有了这些坐标 我们就可以使用图表代理 来找到相应的日期值 最后将其设置为 SwiftUI 状态 来记录当前日期范围 有了范围的状态 就能在图表中 定义一个矩形标记 将当前选定的日期范围可视化 这个状态也可以用来控制 App 的其他部分 例如用来过滤 图表下方详情视图的内容 这是一个简单的例子 用来说明图表代理的用途 它能用来构建许多有趣的功能 例如这个交互式图表 显示所选日期和销售量的值 可以覆盖一个棒棒糖图层 本节课程讨论了 如何通过组合标记创建图表 如何使用标记属性绘制数据 以及如何自定义图表 您还可以收看设计教程 了解如何使用图表 设计出色的 App 界面 以及如何高效设计图表 我们相信您会喜欢 利用 Swift Charts 来构建数据可视化 感谢收看 ♪
-
-
3:48 - Top style chart
import SwiftUI import Charts struct TopStyleChart: View { let data = [ (name: "Cachapa", sales: 916), (name: "Injera", sales: 850), (name: "Crêpe", sales: 802), (name: "Jian Bing", sales: 753), (name: "Dosa", sales: 654), (name: "American", sales: 618) ] var body: some View { Chart(data, id: \.name) { BarMark( x: .value("Sales", $0.sales), y: .value("Name", $0.name) ) // Set the foreground style of the bars. .foregroundStyle(.pink) // Customize the accessibility label and value. .accessibilityLabel($0.name) .accessibilityValue("\($0.sales) sold") } } }
-
5:12 - Daily sales chart
struct DailySalesChart: View { var body: some View { Chart { ForEach(dailySales, id: \.day) { // Try change to LineMark. BarMark( x: .value("Day", $0.day, unit: .day), y: .value("Sales", $0.sales) ) } } } let dailySales: [(day: Date, sales: Int)] = [ (day: date(year: 2022, month: 5, day: 8), sales: 168), (day: date(year: 2022, month: 5, day: 9), sales: 117), (day: date(year: 2022, month: 5, day: 10), sales: 106), (day: date(year: 2022, month: 5, day: 11), sales: 119), (day: date(year: 2022, month: 5, day: 12), sales: 109), (day: date(year: 2022, month: 5, day: 13), sales: 104), (day: date(year: 2022, month: 5, day: 14), sales: 196), (day: date(year: 2022, month: 5, day: 15), sales: 172), (day: date(year: 2022, month: 5, day: 16), sales: 122), (day: date(year: 2022, month: 5, day: 17), sales: 115), (day: date(year: 2022, month: 5, day: 18), sales: 138), (day: date(year: 2022, month: 5, day: 19), sales: 110), (day: date(year: 2022, month: 5, day: 20), sales: 106), (day: date(year: 2022, month: 5, day: 21), sales: 187), (day: date(year: 2022, month: 5, day: 22), sales: 187), (day: date(year: 2022, month: 5, day: 23), sales: 119), (day: date(year: 2022, month: 5, day: 24), sales: 160), (day: date(year: 2022, month: 5, day: 25), sales: 144), (day: date(year: 2022, month: 5, day: 26), sales: 152), (day: date(year: 2022, month: 5, day: 27), sales: 148), (day: date(year: 2022, month: 5, day: 28), sales: 240), (day: date(year: 2022, month: 5, day: 29), sales: 242), (day: date(year: 2022, month: 5, day: 30), sales: 173), (day: date(year: 2022, month: 5, day: 31), sales: 143), (day: date(year: 2022, month: 6, day: 1), sales: 137), (day: date(year: 2022, month: 6, day: 2), sales: 123), (day: date(year: 2022, month: 6, day: 3), sales: 146), (day: date(year: 2022, month: 6, day: 4), sales: 214), (day: date(year: 2022, month: 6, day: 5), sales: 250), (day: date(year: 2022, month: 6, day: 6), sales: 146) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: .init(year: year, month: month, day: day)) ?? Date() }
-
6:16 - Sales by location with line mark
struct LocationsChart: View { var body: some View { Chart { ForEach(seriesData, id: \.city) { series in ForEach(series.data, id: \.weekday) { LineMark( x: .value("Weekday", $0.weekday, unit: .day), y: .value("Sales", $0.sales) ) } .foregroundStyle(by: .value("City", series.city)) .symbol(by: .value("City", series.city)) .interpolationMethod(.catmullRom) } } } let seriesData = [ ( city: "Cupertino", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 54), (weekday: date(year: 2022, month: 5, day: 3), sales: 42), (weekday: date(year: 2022, month: 5, day: 4), sales: 88), (weekday: date(year: 2022, month: 5, day: 5), sales: 49), (weekday: date(year: 2022, month: 5, day: 6), sales: 42), (weekday: date(year: 2022, month: 5, day: 7), sales: 125), (weekday: date(year: 2022, month: 5, day: 8), sales: 67) ] ), ( city: "San Francisco", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 81), (weekday: date(year: 2022, month: 5, day: 3), sales: 90), (weekday: date(year: 2022, month: 5, day: 4), sales: 52), (weekday: date(year: 2022, month: 5, day: 5), sales: 72), (weekday: date(year: 2022, month: 5, day: 6), sales: 84), (weekday: date(year: 2022, month: 5, day: 7), sales: 84), (weekday: date(year: 2022, month: 5, day: 8), sales: 137) ] ) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
7:19 - Sales by location with bar mark
struct LocationsChart: View { var body: some View { Chart { ForEach(seriesData, id: \.city) { series in ForEach(series.data, id: \.weekday) { BarMark( x: .value("Weekday", $0.weekday, unit: .day), y: .value("Sales", $0.sales) ) } .foregroundStyle(by: .value("City", series.city)) .position(by: .value("City", series.city)) } } } let seriesData = [ ( city: "Cupertino", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 54), (weekday: date(year: 2022, month: 5, day: 3), sales: 42), (weekday: date(year: 2022, month: 5, day: 4), sales: 88), (weekday: date(year: 2022, month: 5, day: 5), sales: 49), (weekday: date(year: 2022, month: 5, day: 6), sales: 42), (weekday: date(year: 2022, month: 5, day: 7), sales: 125), (weekday: date(year: 2022, month: 5, day: 8), sales: 67) ] ), ( city: "San Francisco", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 81), (weekday: date(year: 2022, month: 5, day: 3), sales: 90), (weekday: date(year: 2022, month: 5, day: 4), sales: 52), (weekday: date(year: 2022, month: 5, day: 5), sales: 72), (weekday: date(year: 2022, month: 5, day: 6), sales: 84), (weekday: date(year: 2022, month: 5, day: 7), sales: 84), (weekday: date(year: 2022, month: 5, day: 8), sales: 137) ] ) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
8:02 - Monthly sales with line and area marks
struct MonthlySalesChart: View { var body: some View { Chart { ForEach(data, id: \.month) { AreaMark( x: .value("Month", $0.month, unit: .month), yStart: .value("Daily Min", $0.dailyMin), yEnd: .value("Daily Max", $0.dailyMax) ) .opacity(0.3) LineMark( x: .value("Month", $0.month, unit: .month), y: .value("Daily Average", $0.dailyAverage) ) } } } let data = [ (month: date(year: 2021, month: 7), sales: 3952, dailyAverage: 127, dailyMin: 95, dailyMax: 194), (month: date(year: 2021, month: 8), sales: 4044, dailyAverage: 130, dailyMin: 96, dailyMax: 189), (month: date(year: 2021, month: 9), sales: 3930, dailyAverage: 131, dailyMin: 101, dailyMax: 184), (month: date(year: 2021, month: 10), sales: 4217, dailyAverage: 136, dailyMin: 96, dailyMax: 193), (month: date(year: 2021, month: 11), sales: 4006, dailyAverage: 134, dailyMin: 104, dailyMax: 202), (month: date(year: 2021, month: 12), sales: 3994, dailyAverage: 129, dailyMin: 96, dailyMax: 190), (month: date(year: 2022, month: 1), sales: 4202, dailyAverage: 136, dailyMin: 96, dailyMax: 203), (month: date(year: 2022, month: 2), sales: 3749, dailyAverage: 134, dailyMin: 98, dailyMax: 200), (month: date(year: 2022, month: 3), sales: 4329, dailyAverage: 140, dailyMin: 104, dailyMax: 218), (month: date(year: 2022, month: 4), sales: 4084, dailyAverage: 136, dailyMin: 93, dailyMax: 221), (month: date(year: 2022, month: 5), sales: 4559, dailyAverage: 147, dailyMin: 104, dailyMax: 242), (month: date(year: 2022, month: 6), sales: 1023, dailyAverage: 170, dailyMin: 120, dailyMax: 250) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
8:46 - Monthly sales with bar and rectangle marks
struct MonthlySalesChart: View { var body: some View { Chart { ForEach(data, id: \.month) { BarMark( x: .value("Month", $0.month, unit: .month), yStart: .value("Daily Min", $0.dailyMin), yEnd: .value("Daily Max", $0.dailyMax), width: .ratio(0.6) ) .opacity(0.3) RectangleMark( x: .value("Month", $0.month, unit: .month), y: .value("Daily Average", $0.dailyAverage), width: .ratio(0.6), height: 2 ) } } } let data = [ (month: date(year: 2021, month: 7), sales: 3952, dailyAverage: 127, dailyMin: 95, dailyMax: 194), (month: date(year: 2021, month: 8), sales: 4044, dailyAverage: 130, dailyMin: 96, dailyMax: 189), (month: date(year: 2021, month: 9), sales: 3930, dailyAverage: 131, dailyMin: 101, dailyMax: 184), (month: date(year: 2021, month: 10), sales: 4217, dailyAverage: 136, dailyMin: 96, dailyMax: 193), (month: date(year: 2021, month: 11), sales: 4006, dailyAverage: 134, dailyMin: 104, dailyMax: 202), (month: date(year: 2021, month: 12), sales: 3994, dailyAverage: 129, dailyMin: 96, dailyMax: 190), (month: date(year: 2022, month: 1), sales: 4202, dailyAverage: 136, dailyMin: 96, dailyMax: 203), (month: date(year: 2022, month: 2), sales: 3749, dailyAverage: 134, dailyMin: 98, dailyMax: 200), (month: date(year: 2022, month: 3), sales: 4329, dailyAverage: 140, dailyMin: 104, dailyMax: 218), (month: date(year: 2022, month: 4), sales: 4084, dailyAverage: 136, dailyMin: 93, dailyMax: 221), (month: date(year: 2022, month: 5), sales: 4559, dailyAverage: 147, dailyMin: 104, dailyMax: 242), (month: date(year: 2022, month: 6), sales: 1023, dailyAverage: 170, dailyMin: 120, dailyMax: 250) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
9:19 - Monthly sales with average line and annotation
struct MonthlySalesChart: View { var body: some View { Chart { ForEach(data, id: \.month) { BarMark( x: .value("Month", $0.month, unit: .month), yStart: .value("Daily Min", $0.dailyMin), yEnd: .value("Daily Max", $0.dailyMax), width: .ratio(0.6) ) .opacity(0.3) RectangleMark( x: .value("Month", $0.month, unit: .month), y: .value("Daily Average", $0.dailyAverage), width: .ratio(0.6), height: 2 ) } .foregroundStyle(.gray.opacity(0.5)) RuleMark( y: .value("Average", averageValue) ) .lineStyle(StrokeStyle(lineWidth: 3)) .annotation(position: .top, alignment: .leading) { Text("Average: \(averageValue, format: .number)") .font(.headline) .foregroundStyle(.blue) } } } let data = [ (month: date(year: 2021, month: 7), sales: 3952, dailyAverage: 127, dailyMin: 95, dailyMax: 194), (month: date(year: 2021, month: 8), sales: 4044, dailyAverage: 130, dailyMin: 96, dailyMax: 189), (month: date(year: 2021, month: 9), sales: 3930, dailyAverage: 131, dailyMin: 101, dailyMax: 184), (month: date(year: 2021, month: 10), sales: 4217, dailyAverage: 136, dailyMin: 96, dailyMax: 193), (month: date(year: 2021, month: 11), sales: 4006, dailyAverage: 134, dailyMin: 104, dailyMax: 202), (month: date(year: 2021, month: 12), sales: 3994, dailyAverage: 129, dailyMin: 96, dailyMax: 190), (month: date(year: 2022, month: 1), sales: 4202, dailyAverage: 136, dailyMin: 96, dailyMax: 203), (month: date(year: 2022, month: 2), sales: 3749, dailyAverage: 134, dailyMin: 98, dailyMax: 200), (month: date(year: 2022, month: 3), sales: 4329, dailyAverage: 140, dailyMin: 104, dailyMax: 218), (month: date(year: 2022, month: 4), sales: 4084, dailyAverage: 136, dailyMin: 93, dailyMax: 221), (month: date(year: 2022, month: 5), sales: 4559, dailyAverage: 147, dailyMin: 104, dailyMax: 242), (month: date(year: 2022, month: 6), sales: 1023, dailyAverage: 170, dailyMin: 120, dailyMax: 250) ] let averageValue = 137 } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
13:54 - Chart with custom scales for Y and foreground style
struct LocationsChart: View { var body: some View { Chart { ForEach(seriesData, id: \.city) { series in ForEach(series.data, id: \.weekday) { LineMark( x: .value("Weekday", $0.weekday, unit: .day), y: .value("Sales", $0.sales) ) } .foregroundStyle(by: .value("City", series.city)) .symbol(by: .value("City", series.city)) .interpolationMethod(.catmullRom) } } .chartYScale(domain: 0 ... 200) .chartForegroundStyleScale([ "San Francisco": .orange, "Cupertino": .pink ]) } let seriesData = [ ( city: "Cupertino", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 54), (weekday: date(year: 2022, month: 5, day: 3), sales: 42), (weekday: date(year: 2022, month: 5, day: 4), sales: 88), (weekday: date(year: 2022, month: 5, day: 5), sales: 49), (weekday: date(year: 2022, month: 5, day: 6), sales: 42), (weekday: date(year: 2022, month: 5, day: 7), sales: 125), (weekday: date(year: 2022, month: 5, day: 8), sales: 67) ] ), ( city: "San Francisco", data: [ (weekday: date(year: 2022, month: 5, day: 2), sales: 81), (weekday: date(year: 2022, month: 5, day: 3), sales: 90), (weekday: date(year: 2022, month: 5, day: 4), sales: 52), (weekday: date(year: 2022, month: 5, day: 5), sales: 72), (weekday: date(year: 2022, month: 5, day: 6), sales: 84), (weekday: date(year: 2022, month: 5, day: 7), sales: 84), (weekday: date(year: 2022, month: 5, day: 8), sales: 137) ] ) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
15:16 - Chart with custom X axis
struct MonthlySalesChart: View { var body: some View { Chart(data, id: \.month) { BarMark( x: .value("Month", $0.month, unit: .month), y: .value("Sales", $0.sales) ) } .chartXAxis { AxisMarks( values: .stride(by: .month) ) { value in AxisGridLine() AxisTick() AxisValueLabel( format: .dateTime.month(.narrow) ) } } } let data = [ (month: date(year: 2021, month: 7), sales: 3952), (month: date(year: 2021, month: 8), sales: 4044), (month: date(year: 2021, month: 9), sales: 3930), (month: date(year: 2021, month: 10), sales: 4217), (month: date(year: 2021, month: 11), sales: 4006), (month: date(year: 2021, month: 12), sales: 3994), (month: date(year: 2022, month: 1), sales: 4202), (month: date(year: 2022, month: 2), sales: 3749), (month: date(year: 2022, month: 3), sales: 4329), (month: date(year: 2022, month: 4), sales: 4084), (month: date(year: 2022, month: 5), sales: 4559), (month: date(year: 2022, month: 6), sales: 1023) ] let averageValue = 137 } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
16:17 - Chart with custom X axis and conditional content for axis marks
struct MonthlySalesChart: View { var body: some View { Chart(data, id: \.month) { BarMark( x: .value("Month", $0.month, unit: .month), y: .value("Sales", $0.sales) ) } .chartXAxis { AxisMarks(values: .stride(by: .month)) { value in if value.as(Date.self)!.isFirstMonthOfQuarter { AxisGridLine().foregroundStyle(.black) AxisTick().foregroundStyle(.black) AxisValueLabel( format: .dateTime.month(.narrow) ) } else { AxisGridLine() } } } } let data = [ (month: date(year: 2021, month: 7), sales: 3952), (month: date(year: 2021, month: 8), sales: 4044), (month: date(year: 2021, month: 9), sales: 3930), (month: date(year: 2021, month: 10), sales: 4217), (month: date(year: 2021, month: 11), sales: 4006), (month: date(year: 2021, month: 12), sales: 3994), (month: date(year: 2022, month: 1), sales: 4202), (month: date(year: 2022, month: 2), sales: 3749), (month: date(year: 2022, month: 3), sales: 4329), (month: date(year: 2022, month: 4), sales: 4084), (month: date(year: 2022, month: 5), sales: 4559), (month: date(year: 2022, month: 6), sales: 1023) ] let averageValue = 137 } extension Date { var isFirstMonthOfQuarter: Bool { Calendar.current.component(.month, from: self) % 3 == 1 } } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
17:00 - Chart with custom Y axis
struct MonthlySalesChart: View { var body: some View { Chart(data, id: \.month) { BarMark( x: .value("Month", $0.month, unit: .month), y: .value("Sales", $0.sales) ) } .chartYAxis { AxisMarks( preset: .extended, position: .leading) } } let data = [ (month: date(year: 2021, month: 7), sales: 3952), (month: date(year: 2021, month: 8), sales: 4044), (month: date(year: 2021, month: 9), sales: 3930), (month: date(year: 2021, month: 10), sales: 4217), (month: date(year: 2021, month: 11), sales: 4006), (month: date(year: 2021, month: 12), sales: 3994), (month: date(year: 2022, month: 1), sales: 4202), (month: date(year: 2022, month: 2), sales: 3749), (month: date(year: 2022, month: 3), sales: 4329), (month: date(year: 2022, month: 4), sales: 4084), (month: date(year: 2022, month: 5), sales: 4559), (month: date(year: 2022, month: 6), sales: 1023) ] let averageValue = 137 } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
18:26 - Chart with plot area style
struct TopStyleChart: View { var body: some View { Chart(data, id: \.name) { BarMark( x: .value("Sales", $0.sales), y: .value("Name", $0.name) ) // Set the foreground style of the bars. .foregroundStyle(.pink) // Customize the accessibility label and value. .accessibilityLabel($0.name) .accessibilityValue("\($0.sales) sold") } .chartPlotStyle { plotArea in plotArea.frame(height: 60 * 6) .background(.pink.opacity(0.2)) .border(.pink, width: 1) } } let data = [ (name: "Cachapa", sales: 916), (name: "Injera", sales: 850), (name: "Crêpe", sales: 802), (name: "Jian Bing", sales: 753), (name: "Dosa", sales: 654), (name: "American", sales: 618) ] }
-
20:03 - Chart with brushing interaction
struct InteractiveBrushingChart: View { @State var range: (Date, Date)? = nil var body: some View { Chart { ForEach(data, id: \.day) { LineMark( x: .value("Month", $0.day, unit: .day), y: .value("Sales", $0.sales) ) .interpolationMethod(.catmullRom) .symbol(Circle().strokeBorder(lineWidth: 2)) } if let (start, end) = range { RectangleMark( xStart: .value("Selection Start", start), xEnd: .value("Selection End", end) ) .foregroundStyle(.gray.opacity(0.2)) } } .chartOverlay { proxy in GeometryReader { nthGeoItem in Rectangle().fill(.clear).contentShape(Rectangle()) .gesture(DragGesture() .onChanged { value in // Find the x-coordinates in the chart’s plot area. let xStart = value.startLocation.x - nthGeoItem[proxy.plotAreaFrame].origin.x let xCurrent = value.location.x - nthGeoItem[proxy.plotAreaFrame].origin.x // Find the date values at the x-coordinates. if let dateStart: Date = proxy.value(atX: xStart), let dateCurrent: Date = proxy.value(atX: xCurrent) { range = (dateStart, dateCurrent) } } .onEnded { _ in range = nil } // Clear the state on gesture end. ) } } } let data: [(day: Date, sales: Int)] = [ (day: date(year: 2022, month: 5, day: 8), sales: 168), (day: date(year: 2022, month: 5, day: 9), sales: 117), (day: date(year: 2022, month: 5, day: 10), sales: 106), (day: date(year: 2022, month: 5, day: 11), sales: 119), (day: date(year: 2022, month: 5, day: 12), sales: 109), (day: date(year: 2022, month: 5, day: 13), sales: 104), (day: date(year: 2022, month: 5, day: 14), sales: 196), (day: date(year: 2022, month: 5, day: 15), sales: 172), (day: date(year: 2022, month: 5, day: 16), sales: 122), (day: date(year: 2022, month: 5, day: 17), sales: 115), (day: date(year: 2022, month: 5, day: 18), sales: 138), (day: date(year: 2022, month: 5, day: 19), sales: 110), (day: date(year: 2022, month: 5, day: 20), sales: 106), (day: date(year: 2022, month: 5, day: 21), sales: 187), (day: date(year: 2022, month: 5, day: 22), sales: 187), (day: date(year: 2022, month: 5, day: 23), sales: 119), (day: date(year: 2022, month: 5, day: 24), sales: 160), (day: date(year: 2022, month: 5, day: 25), sales: 144), (day: date(year: 2022, month: 5, day: 26), sales: 152), (day: date(year: 2022, month: 5, day: 27), sales: 148), (day: date(year: 2022, month: 5, day: 28), sales: 240), (day: date(year: 2022, month: 5, day: 29), sales: 242), (day: date(year: 2022, month: 5, day: 30), sales: 173), (day: date(year: 2022, month: 5, day: 31), sales: 143), (day: date(year: 2022, month: 6, day: 1), sales: 137), (day: date(year: 2022, month: 6, day: 2), sales: 123), (day: date(year: 2022, month: 6, day: 3), sales: 146), (day: date(year: 2022, month: 6, day: 4), sales: 214), (day: date(year: 2022, month: 6, day: 5), sales: 250), (day: date(year: 2022, month: 6, day: 6), sales: 146) ] } func date(year: Int, month: Int, day: Int = 1) -> Date { Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) ?? Date() }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。