大多数浏览器和
Developer App 均支持流媒体播放。
-
进一步了解 WidgetKit 中的复杂功能
探索如何利用 WidgetKit 创建美观的表盘复杂功能。我们将介绍 WidgetKit 中的 watchOS 专用功能,并帮助您从现有的 ClockKit 复杂功能迁移到其他地方。要了解有关 WidgetKit 的更多信息,请观看 WWDC22 的“复杂功能和小组件:重新载入”。
资源
- Creating accessory widgets and watch complications
- Emoji Rangers: Supporting Live Activities, interactivity, and animations
- Migrating ClockKit complications to WidgetKit
- WidgetKit
相关视频
Tech Talks
WWDC22
WWDC20
-
下载
♪ ♪ August Joki: 大家好 我是 August Joki 一名 watchOS 软件工程师 我将在这里向你展示如何进一步 使用 WidgetKit 的复杂功能 希望你已经看到了精彩的 Complications 和 Widgets 即 Reloaded talk 首先 介绍了 WidgetKit 复杂功能的基本知识 本讲座详细阐述了其中涉及的概念 因为它们与表盘的复杂功能有关 我的 WWDC 2020 讲座 在 SwiftUI 中 构建复杂功能涵盖了更多关于着色 和 SwiftUI 绘制复杂功能的细节 在这个讲座中 我将讨论 WatchOS 独有的 WidgetKit 功能 以及如何将你和你的用户现有的 ClockKit 复杂功能迁移到 WidgetKit 我从 Coffee Tracker 示例 App 中 获得了灵感 并将其作为整个讲座的一个示例 该 App 会记录你一天中喝的 咖啡 茶和苏打水的数量 并跟踪你体内的咖啡因含量 先说说 watchOS 的独特之处 在 iOS 16 中 我们在手机的锁屏中 加入了复杂功能 在 WatchOS 9 中 我们在手表的 复杂功能中引入了 WidgetKit 在表盘上 我们为腕表屏幕的各个角落 设计了独特的复杂功能显示 而且它需要一个名为 accessoryCorner 的 独特的 WidgetKit 系列来描述它 这种独特呈现的一部分是由 SwiftUI 视图指定的辅助内容 但并不作为内容的一部分进行渲染 相反 它是由表盘渲染的
角落的圆形部分是标准的 SwiftUI 渲染 辅助内容是角落中的弯曲部分
或者在图文表盘的刻度盘上
accessoryInline 系列 在表盘上有着独特的表现 根据表盘的不同 它有多种渲染方式 有时是平的 有时是弯曲的 以匹配刻度盘
让我们来讨论一下 如何支持这些独特的功能 看看如何通过更新 coffee tracker app 来使用 WidgetKit
除了 iOS 16 上三个新的 复杂功能的小组件系列 AccessoryRectangular AccessoryCircle 和 AccessoryInline 外 我们在 watch OS 9 上还有 第四个系列 叫做 AccessoryCorner
accessoryCorner 可以显示为 较大的圆形内容 如下方角落中显示的地图 和心率复杂内容 也可以显示为较小的圆形内容 带有弯曲的标签或仪表 如上角显示的 coffee tracker 和月相复杂功能
为了控制是否显示内部辅助内容 watchOS 9 增加了一个新的 视图修改器 我现在为你展示一下
让我们看看如何为我的 coffee tracker app 构建一个角落复杂功能
从较大的圆形内容样式开始 我有一个带有 SF Symbol 和背景的 ZStack SwiftUI 的内容会自动裁剪成一个圆形 与其他角落复杂功能设计保持一致
为了添加内部弯曲的内容 我们使用了新的 watchOS widgetLabel 视图修改器 表盘提取修改器的内容 以绘制适合表盘系列和风格的控件 圆形的内容会自动缩小以腾出空间 对于 accessoryCorner 你可以在小组件的标签中 指定 SwiftUI 文本 仪表 或 progressView
AccessoryCorner 并不是唯一支持 WidgetLabel 的系列 我们来看看它是如何在 accessoryCircular 系列中使用的 在图文表盘上 除了角落复杂功能 表盘内部还有四个圆形复杂功能 我的 coffee tracker 圆形复杂功能 在中间顶部 看起来非常类似于 我们刚才看到的角落复杂功能 但在刻度盘上有文本 现在 我将演示如何添加该文本
对于圆形复杂功能设计 我认为更合适的做法是 将 widgetLabel 中的标尺移动到 角落复杂功能的前面和中心位置 为了利用图文的顶部中间位置 我向仪表添加了一个 widgetLabel 以便在较长的边框区域中 显示额外的文本 否则这些文本 将无法与圆形内容贴合 但现在我在主视图和上面的文本之间 有了多余的信息 我可以通过将圆形内容切换到 那个好看的咖啡杯 SF Symbol 来清理它 但是当我切换到一个没有边框的 圆形复杂功能时 我就失去了所有的咖啡因信息 幸运的是 我可以添加一段 API 使我的复杂功能在这两种情况下 都能正常工作
我更新了我的复杂功能 将名为 showsWidgetLabel 的环境属性 添加到我的视图中 当复杂功能出现在显示小组件 标签内容的表盘位置时 就会出现这种情况
然后我可以根据 showsWidgetLabel 的值来更改内容 以便在每个复杂功能点中 获得适当级别的信息 我刚刚展示了 accessoryCircular 系列在表盘上的 两种不同展示方式 还有一种方式你需要注意 超大表盘一直以来都是 人们以超大格式 查看时间的绝佳方式 它支持单一的 大型圆形复杂功能 超大表盘使用 accessoryCircular 系列 并自动放大内容以匹配表盘样式 请注意 由于这个表盘是为 单一 大型的复杂功能而设计的 不要利用增加画布尺寸的机会 来密集地包装你的复杂功能 内容应该与普通的圆形系列相同 只是更大一些 正如我前面提到的 手表表盘上还有两个 小组件系列 accessoryRectangular 和 accessoryInline 没有显示 widgetLabel 的 矩形复杂功能 而 accessoryInline 系列已经充当了 widgetLabel 的角色 表盘从你的内嵌内容中 提取图像和文本 并根据表盘的外观进行渲染 接下来是迁移 迁移包括两部分 在 WidgetKit 中重写现有的 ClockKit 复杂功能代码 并提供一个映射 让系统知道如何升级 人们在手表上设置的复杂功能 当你采用 WidgetKit 时 系统将停止向你的 ClockKit 数据源 询问新内容 并在表盘编辑选择器中 仅显示你的新复杂功能
除了将 WidgetKit 引入手表之外 watchOS 9 还更新了每一张表盘 这使我们能够 将复杂功能系列的数量 从 12 个大幅减少到只有 4 个 矩形和角落直接映射到 accessoryRectangular 和 accessoryCorner 所有三个图形 ClockKit 系列 现在都是单一 accessoryCircular WidgetKit 系列 而且在使用旧的 utilitarianSmallFlat 或 utilitarianLarge 的地方 也可以使用 accessoryInline 系列
许多过去使用 utilitarianSmall 的地方 现在已经更新为使用 accessoryCorner 系列
有了 WidgetKit SwiftUI 视图 和它们的状态驱动布局 已经取代了 ClockKit 的模板 WidgetKit 仍有熟悉的时间表和条目 事实上 它们最初的灵感来自 ClockKit 本身 这意味着你的复杂功能数据源 将完好地迁移到 静态或基于意图的 WidgetKit 配置之一
关于 WidgetKit 支持的配置类型 以及一般系列支持的更多详细信息 请参阅原始的 WidgetKit 课程 我们向 ClockKit 添加了 最后一个 API 允许系统自动迁移个人的复杂功能 这使得你现有的复杂功能 已经在表盘上自动升级到 新的 WidgetKit 复杂功能 无需任何用户交互 当你的 App 在手表上更新时 表盘会检查你的 App 包中 是否存在小组件 如果发现任何小组件 它将启动你的 ClockKit 复杂功能数据源 为现有复杂功能生成迁移 从此时开始 只有当某人收到 带有 ClockKit 复杂功能的 共享表盘时才会运行你的 CLKComplicationDataSource 来请求迁移 每次共享新表盘时 系统都会要求你进行迁移 因此 为了获得一致的体验 你应该保持迁移的一致性 一旦你完成了漂亮的 WidgetKit 复杂功能创建 就可以添加新的属性 widgetMigrator 以提供符合新的 Migrator 协议的对象 可以是你的复杂功能数据源本身 也可以是你提供的其他类型
CLKComplication WidgetMigrator 协议 具有一个功能 即从现有的 CLKComplicationDescriptors 向表盘小组件迁移配置 采用新 API 的最直接方法是 让数据源遵守新的 Migrator 协议
如果 WidgetKit 复杂功能 使用静态配置 则需要提供静态迁移配置 如果你在小组件复杂功能中 使用意图 就会有一个等效的迁移配置 请注意 如果你提供 基于意图的迁移配置 那么你就还需要 在手表 App 和小组件扩展中 包含你的意图定义 以便你可以在这两个位置 创建意图对象 WidgetKit 能够以创新的方式 为手表制造复杂功能 同时显著简化使用体验 感谢观看
-
-
3:06 - Large Corner
struct CornerView: View { let value: Double var body: some View { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } } }
-
3:27 - Corner with Gauge
struct CornerView: View { let value: Double var body: some View { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } minimumValueLabel: { Text("0") } maximumValueLabel: { Text("500") } } } }
-
4:24 - Circular Gauge
struct CircularView: View { let value: Double var body: some View { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) } }
-
4:34 - Circular Gauge with Widget Label
struct CircularView: View { let value: Double var body: some View { let mg = value.inMG() Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
4:51 - Circular Stack with Widget Label
struct CircularView: View { let value: Double var body: some View { let mg = value.inMG() ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
5:12 - Circular Stack or Gauge
struct CircularView: View { let value: Double @Environment(\.showsWidgetLabel) var showsWidgetLabel var body: some View { let mg = value.inMG() if showsWidgetLabel { ZStack { AccessoryWidgetBackground() Image(systemName: "cup.and.saucer.fill") .font(.title.bold()) .widgetAccentable() } .widgetLabel { Text("\(mg, formatter: mgFormatter) Caffeine") } } else { Gauge(value: value, in: 0...500) { Text("MG") } currentValueLabel: { Text("\(Int(value))") } .gaugeStyle(.circular) } } var mgFormatter: Formatter { let formatter = MeasurementFormatter() formatter.unitOptions = [.providedUnit] return formatter } } extension Double { func inMG() -> Measurement<UnitMass> { Measurement<UnitMass>(value: self, unit: .milligrams) } }
-
9:47 - Widget Migrator
var widgetMigrator: CLKComplicationWidgetMigrator { self }
-
9:56 - Static Migration Configuration
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { CLKComplicationStaticWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle) }
-
10:03 - Intent Migration Configuration
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { CLKComplicationIntentWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle, intent: intent, localizedDisplayName: "Coffee Tracker") }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。