大多数浏览器和
Developer App 均支持流媒体播放。
-
复杂功能和小组件:重新载入
我们将在探索 watchOS 和 iOS 锁屏时再次推出“小组件边看边写”活动。了解 WidgetKit 的最新改进,它们可为 watchOS 上的复杂功能提供支持,帮助您为 iPhone 构建锁屏小组件。我们将介绍如何整合最新的 SwiftUI 视图以提供一目了然的数据,探索每个平台如何渲染内容,并学习如何在小组件或复杂功能中对您的内容的设计和外观进行自定义。
资源
- Creating accessory widgets and watch complications
- Emoji Rangers: Supporting Live Activities, interactivity, and animations
- WidgetKit
相关视频
WWDC23
Tech Talks
WWDC22
WWDC21
WWDC20
-
下载
♪ 柔和乐器演奏的嘻哈音乐 ♪ ♪ Devon Endicott:嗨 我的名字叫 Devon 我是 watchOS 团队的 工程师 今天我将谈谈 iOS Graham Perks:我是 iOS 工程师 Graham 今天我将谈谈 watchOS Devon:我们将讨论 向 WidgetKit 添加的 API 它们使您能为 watchOS 的 锁屏和复杂功能 编写附件小组件 我们将展示如何同时开发这两者 以及 SwiftUI 的新增功能 以帮助您完成开发过程 如果您不熟悉小组件 时间线和重新加载 我鼓励您查找以前的 WidgetKit 讲座 首先 我们将谈谈复杂功能的历史 及其演变过程 然后 我们将讨论为您的小组件 着色的新 API 以及新环境中的复杂功能 之后 Graham 将演示如何开始 制作您自己的小组件 并将现有的小组件扩展 移动到 watchOS 接下来 Graham 将带您了解 如何充分利用这些较小的视图 最后 我们将讨论您的小组件 可能出现的 不同隐私环境 复杂功能是 watchOS 平台的关键部分 可在表盘上呈现快速 一目了然的信息 它们传达可立即访问的 高价值信息 您可以一键访问 App 中的 相关位置 在 watchOS 2 中 ClockKit 让您可以 创建自己的复杂功能 从那时起 复杂功能走过了 漫长的道路
watchOS 5 推出了 丰富的复杂功能 包括图形内容和一套新的 小组件系列 watchOS 7 推出了 SwiftUI 复杂功能 和多个其他复杂功能 使您能够将复杂功能提升到 一个新的水平 并提供比以往更多的选项 如今 我们通过 WidgetKit 对复杂功能进行了重新构思 和重新制作 并将 SwiftUI 纳入到复杂功能中 以小组件的形式为 iOS 带来了 一目了然的复杂功能体验 通过 iOS 16 和 watchOS 9 中的 WidgetKit 您能够在两个平台上构建一目了然的 优质小组件 和复杂功能 从而可以只编写一次代码 并与现有的主屏幕小组件 共享基础结构 为此 我们在以“accessory” 一词为前缀的 现有 WidgetFamily 类型中 添加了新的小组件系列 新的 accessoryRectangular 系列 可用于显示多行文本或小图形和图表 类似于现有的 ClockKit graphicRectangular 系列 accessoryCircular 系列 非常适合简要的信息 仪表和进度视图 该系列还取代了 graphicCircular ClockKit 系列 全新的 accessoryInline 是一个文本槽 出现在 watchOS 的许多表盘上 以及 iOS 的时间上方 内嵌槽有多种尺寸 稍后我们将讨论 如何充分利用它们
watchOS 特有的是 新的 accessoryCorner 系列 它将一小圈小组件内容与仪表和文本 混合在一起 本讲的重点是 iOS 和 watchOS 之间通用的系列 如要详细了解这个新的 watchOS 系列 以及复杂功能的特性 请查看 “进一步了解 WidgetKit 复杂功能”讲座 让我们谈谈颜色和渲染模式 您可能已经注意到 附件小组件呈现出几种不同的外观 附件系列小组件的外观由系统控制 我们提供了一些工具 以帮助它们适应渲染风格 您的小组件可能会显示在 三种不同的渲染模式中 即全彩、强调或鲜艳模式 我们引入了 WidgetRenderingMode 类型 来表示这三种不同的呈现方式 您可以使用 widgetRenderingMode 键路径 从 Environment 访问此值 之后 您可以有条件地更改内容 以确保它在出现的任何地方 看起来都恰到好处 在 watchOS 的全彩模式下 您的内容完全按照您指定的方式显示 许多现有的复杂功能在全彩模式下 会呈现出丰富多彩的外观 例如天气仪表中的梯度 或活动环的颜色 在强调渲染模式下 您的视图 分为两组并独立着色 两个着色组都是单色的 仅保留原始不透明度 您可以告诉系统如何 使用 .widgetAccentable() 视图修饰符对视图进行分组 或者根据 Widget Rendering Mode 环境值 切换内容 以确保在展平时呈现完美的外观 请注意 系统可以通过多种方式 为您的内容着色 其中一些方式是颠倒的 有些是黑色背景 而另一些则是 watchOS 9 提供的 新的全彩背景 在 iOS 的鲜艳渲染模式下 您的内容会降低饱和度并适当着色 以适应锁屏背景 系统将您的灰度内容 映射到材料外观 这种材料适应其背后的内容 在环境中看起来恰到好处 此外 可以对锁屏进行配置 赋予鲜艳渲染模式一个彩色的色调 光源色最终大部分不透明且更亮 另一方面 暗源色 在其背后的背景中显得不太突出 只有少量变亮 为确保可识别性 请避免在此模式下使用透明色 反之 请使用较深的颜色或黑色 来表示不太突出的内容 同时保持可识别性 为了帮助您了解其中的一些细微差别 我们还引入了 AccessoryWidgetBackground 视图 以便为需要它的小组件 提供一致的背景 比如这个圆形日历 虽然大多数附件小组件没有背景 但某些样式可通过其中之一来增强 背景视图在各种小组件渲染模式中 呈现出不同的外观 并由系统进行调整 以适合表盘或锁屏的样式 它在全彩和强调模式下是一个 柔和透明的视图 在鲜艳环境中是一个黑色视图 这会导致低亮度和完全模糊 Graham 非常高兴能够开始 为 watchOS 的锁屏和复杂功能 制作一些新的小组件 下面由 Graham 为大家讲解 Graham:嗨 我们又见面了 我将为现有的 App Emoji Rangers 添加对新小组件系列的支持 某些人可能通过 WWDC 2020 的 “Widgets 边看边写”讲座 熟悉了该 App 在我开始之前 对于无小组件项目的开发者 我想说明一点 您可以通过向您的项目添加 小组件扩展目标来入手 小组件扩展目标 已存在于 iOS 上 并且已被引入 watchOS 但我知道开发者中的许多人已经有了 含小组件的 App 所以我们今天就从这开始 讨论如何添加新的小组件 和复杂功能
我们将继续进行 Emoji Rangers 项目 这个 App 会跟踪我们最喜欢的 Emoji Rangers 并使用主屏幕小组件让您及时了解 其健康状况和充电时间 我们已经将 Emoji Rangers 引入 watchOS 将我们最喜欢的 App 带到我们的手腕上 今天 我们将扩展 Emoji Rangers 以支持我们的新小组件系列 并在手表中引入其小组件扩展 首先 我们将小组件扩展 添加到手表上 我们将添加一个与现有 iOS 目标 共享代码的 新 watchOS 目标 我们将复制 iOS 小组件扩展目标
给它一个更好的名称
将捆绑标识符改为 以 Watch App 的 标识符为前缀 把 watchOS 设为目标
并将新扩展嵌入到 Watch App 中 现在我们需要在 watchOS 上 构建我们的代码 让我们继续吧
浏览 EmojiRangerWidget 代码 我们可以看到系统重新加载内容时 使用的时间线提供程序
使用 SwiftUI 为不同系列生成内容的视图
小组件配置 和 Xcode 预览提供程序 Emoji Rangers App 已经支持 iOS 主屏幕小组件 它提供了中小系列的系统 这里在小组件配置中 我将添加新的系列
由于系统系列在手表上不可用 我们需要使用平台宏 来指定我们的 supportedFamilies
在预览提供程序中 我将为新系列添加预览
接下来 我们需要实现新的 IntentRecommendation API 然后才能为 watchOS 成功构建小组件 虽然意图在 iOS 的 小组件编辑 UI 中 是完全可配置的 但在 watchOS 上 我们需要提供一个预配置的列表 我们可以通过覆盖 IntentTimelineProvider 上的 新推荐方法来做到这一点
现在我们正在成功构建小组件 让我们继续预览 看看我们的圆形小组件是什么样子
即使是小组件的内容 也不适合我们的新外形尺寸 新的小组件系列 比主屏幕上的 iOS 小组件更小 您需要考虑复杂功能的内容 现在我们谈谈可以突出复杂功能的 一些新视图 我们来看视图 我们可以看到 systemSmall 和其他小组件的代码 让我们为 accessoryCircular 案例 添加代码 我觉得只要有头像就够了
这提供了进入 App 的快捷方式 但不向用户提供任何信息 让我们在边缘添加一个进度视图 这将使用户了解 游侠何时准备好再次战斗
问题是把这个进度视图制作成 实时动画效果 需要在短时间内连续输入 大量时间线条目 但我们可以使用 SwiftUI 新的自动更新 ProgressView 这需要一个日期区间 在此期间 我们的游侠将完全康复 系统会不断更新我们的进度视图 这意味着我们在这里只需要 一个时间线条目
好多了 现在让我们添加矩形系列
我们将选择矩形预览 这给了我们更多的空间 所以我们将制作 一个复杂功能样式的三行视图 首先是角色的名字 其次是他们的等级 然后是完全康复的时间 为此我们将使用自动更新的日期字段 我想突出角色的名字 所以我将使用标题字体样式 调整文字大小 并添加调整颜色的 widgetAccentable 修饰符
我们的视图在鲜艳模式下看起来很棒 现在让我们看看它在手表的 其他渲染模式下的样子
您可以看到角色的名字如何呈现 强调色 为了使您的小组件和复杂功能 非常适合这一环境 请使用默认字体参数并利用字体样式 这很重要 iOS 和 watchOS 的 字体样式和大小不同 iOS 使用的是常规的文字设计 而 watchOS 使用的是 字体更粗的圆润设计 您的小组件和复杂功能将在屏幕上 与其他小组件和复杂功能相邻 因此它们看起来一致 我们建议使用字体样式 Title Headline Body 和 Caption
Xcode 的预览显示 我们还有剩余空间 来添加头像
我们看看它在 iPhone 上的样子
看起来很棒 最后 我们来添加第三种样式 accessoryInline 它显示一行文字 并选择性地显示图像 请注意 内嵌附件是 根据系统定义的颜色和字体绘制的 我们选择预览
然后显示英雄的名字和充电倒计时
这段文字对于表槽来说太长了 所以现在是向您展示 ViewThatFits 的好时机 我可以提供多个视图 从冗长到简洁 ViewThatFits 将选择 适合可用空间的第一个内容视图 该视图不会发生截断或剪裁的情况 让我们缩短文本
即使这样对于最短的表槽来说 可能也太长了 所以我们通过切换这个名字的头像 来提供第三种选择
我们看看它是什么样子的
有关此内容的更多信息 请参阅 “使用 SwiftUI 编写自定义布局”讲座 太棒了 Emoji Rangers 也希望享有一些隐私 下面请 Devon 继续为大家讲解 Devon:大家好 下面我们谈谈隐私 到目前为止 我们在本讲中讨论了 小组件和复杂功能的活动状态 但是 在我们的平台上 您需要考虑 设备是在编辑内容 还是处于低亮度状态 在 iOS 锁屏上 默认行为是 即使在设备锁定时也显示您的内容 这是此网格中左上单元格的状态 但是 这可以在“设置”中进行配置 用户可以选择在锁定时编辑小组件 就像通知一样 在 watchOS 上 只要手表处于佩戴状态 设备就会保持解锁状态 不活动时 手表转换为永远在线状态 内容以低亮度显示 更新节奏较慢 默认情况下 您的内容不会 在低亮度下编辑 这是左下角的状态 与锁屏一样 用户可以 将复杂功能内容配置为 在永远在线状态下编辑 在这种状态下 您需要确保您的内容 已准备好进行编辑和进入低亮度状态 合起来 这些平台涵盖了此处显示的 所有四种状态 请考虑所有这些可能的状态并确保 您的复杂功能和小组件在所有情况下 都能正常工作 我们谈谈如何做到这一点 在手表上 您的小组件需要支持 永远在线的显示体验 您可以使用 \.isLuminanceReduced 环境值 调整内容以实现永远在线 如果您来自 ClockKit 请注意您现在可以为每个时间线条目 准备永远在线的内容 而不是仅仅一个时间线条目 在永远在线状态下 时间相关文本 和进度视图将变为低保真度模式 以支持永远在线状态的低更新节奏 要支持此模式 请使用环境值 删除任何对时间敏感的内容 并优化您的内容 以适应较低的更新频率 现在我们谈谈编辑 默认情况下 隐私模式将显示 TimelineProvider 创建的 占位符视图的编辑版本 如果您有一些敏感的元素 和不需要编辑的其他元素 可以使用 .privacySensitive 修饰符 只标记一些要编辑的视图 在这个例子中 我们在小组件中编辑了心率 但是没有编辑图像 现在您可以为锁屏和 WidgetKit 复杂功能 制作优质的小组件了 有关 SwiftUI 新功能的 更多信息 请查看 “使用 SwiftUI 编写自定义布局”讲座 两位:谢谢观看 ♪
-
-
4:07 - widgetAccentable
VStack(alignment: .leading) { Text("Headline") .font(.headline) .widgetAccentable() Text("Body 1") Text("Body 2") }.frame(maxWidth: .infinity, alignment: .leading)
-
5:24 - AccessoryWidgetBackground
ZStack { AccessoryWidgetBackground() VStack { Text("MON") Text("6") .font(.title) } }
-
9:02 - Xcode Previews
EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .spouty)) .previewContext(WidgetPreviewContext(family: .accessoryCircular)) .previewDisplayName("Circular") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .spouty)) .previewContext(WidgetPreviewContext(family: .accessoryRectangular)) .previewDisplayName("Rectangular") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .spouty)) .previewContext(WidgetPreviewContext(family: .accessoryInline)) .previewDisplayName("Inline") #if os(iOS)
-
9:38 - recommendations method
return recommendedIntents() .map { intent in return IntentRecommendation(intent: intent, description: intent.hero!.displayString) }
-
11:05 - ProgressView
ProgressView(interval: entry.character.injuryDate...entry.character.fullHealthDate, countdown: false, label: { Text(entry.character.name) }, currentValueLabel: { Avatar(character: entry.character, includeBackground: false) }) .progressViewStyle(.circular)
-
11:26 - Rectangular
case .accessoryRectangular: HStack(alignment: .center, spacing: 0) { VStack(alignment: .leading) { Text(entry.character.name) Text("Level \(entry.character.level)") Text(entry.character.fullHealthDate, style: .timer) }.frame(maxWidth: .infinity, alignment: .leading) Avatar(character: entry.character, includeBackground: false) }
-
14:03 - ViewThatFits
ViewThatFits { Text("\(entry.character.name) is resting, combat-ready in \(entry.character.fullHealthDate, style: .relative)") Text("\(entry.character.name) ready in \(entry.character.fullHealthDate, style: .timer)") Text("\(entry.character.avatar) \(entry.character.fullHealthDate, style: .timer)") }
-
16:18 - isLuminanceReduced
@Environment(\.isLuminanceReduced) var isLuminanceReduced var body: some View { if isLuminanceReduced { Text("🙈").font(.title) } else { Text("🐵").font(.title) } }
-
16:52 - privacySensitive
VStack(spacing: -2) { Image(systemName: "heart") .font(.caption.bold()) .widgetAccentable() Text("\(currentHeartRate)") .font(.title) .privacySensitive() }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。