Highlight
本场 code-along 把第一集的 small widget 扩展到 systemMedium,使用
TimelineReloadPolicy生成治疗完成前的多条 timeline entry,并通过 SiriKit custom intent、widgetURL和 SwiftUILink支持用户配置与深度链接。
核心内容
第一集已经让 Emoji Rangers 的 Power Panda 出现在主屏幕上,但它还只是一个 systemSmall widget。用户打开 Widget Gallery 时,如果一个 widget 支持多种尺寸,系统会让用户选择不同 family。问题是,small 版只够放一个头像,medium 版多出来的空间应该承载更多角色信息。
这场 code-along 先补齐 family 适配,再进入真正的 timeline。角色会随时间恢复生命值,App 已经知道角色什么时候完全恢复。继续让 widget 请求刷新会浪费系统调度机会;更好的做法是在 provider 里一次生成到恢复完成前的 timeline entry,让 WidgetKit 按时间展示。
后半段把这个 widget 从固定角色推进到可配置体验。用户可以在主屏幕长按 widget,进入 Edit Widget,选择 Power Panda、Egghead 或 Spouty。选好角色后,点击 widget 还应该直接进入对应角色详情页。WidgetKit 负责展示,SiriKit custom intent 负责配置,widgetURL 和 SwiftUI Link 负责跳回 App。
详细内容
1. 同一个 widget 支持 small 和 medium
(03:46)第一步是把上一集只支持 systemSmall 的 widget 扩展到 systemMedium。WidgetKit 提供 widgetFamily environment value,视图可以根据当前 family 返回不同布局。
本场没有官方 Code tab 片段。下面只整理 transcript 明确出现的 API 和视图结构,用来说明代码形状。
supportedFamilies([.systemSmall, .systemMedium])
@Environment(\.widgetFamily) var family
switch family {
case .systemSmall:
AvatarView(character: selectedCharacter)
default:
HStack {
AvatarView(character: selectedCharacter)
Text(selectedCharacter.bio)
}
}
关键点:
supportedFamilies从第一集的 single family 扩展到systemSmall和systemMedium。widgetFamily让同一个 entry view 知道自己正在用哪个 family 渲染。systemSmall继续使用AvatarView。- medium 版本把
AvatarView放进HStack,旁边显示角色 bio。 - transcript 还演示了给 small 和 medium 都补背景色与前景色,让预览接近真实主屏幕效果。
2. 用完整 timeline 替代不必要的刷新
(01:12)WidgetKit 的 timeline reload policy 有三种:atEnd、after 和 never。atEnd 会在最后一条 entry 展示后开始调度更新;after 会在指定日期开始调度;never 表示系统不会独立更新,需要开发者通过 Widget Center API 显式 reload。
(07:39)Emoji Rangers 的角色会随时间恢复。既然 App 知道完全恢复的 endDate,provider 可以按一分钟间隔生成 entry,直到角色恢复完成。
let selectedCharacter = configuration.hero
let endDate = selectedCharacter.fullHealthDate
let interval = oneMinute
var currentDate = Date()
var entries: [Entry] = []
while currentDate < endDate {
entries.append(Entry(date: currentDate, character: selectedCharacter))
currentDate += interval
}
Timeline(entries: entries, policy: .atEnd)
关键点:
selectedCharacter是当前 widget 要显示的角色。endDate是角色完全恢复的时间点。- transcript 明确说更新时间间隔是一分钟,从
currentDate开始。 - 每次循环创建一条带
currentDate的 entry,再把时间前进一步。 - timeline 耗尽前,WidgetKit 不需要重新调用 provider 取数据。
atEnd和after都只是最早可刷新时间,系统会根据用户体验和调度情况安排实际刷新。
3. 用 relevance 给 Smart Stack 提示
(09:00)timeline entry 还有一个可选的 relevance。当 widget 位于 stack 中时,系统可以根据这个提示把 widget 轮换到更合适的位置。
let relevance = selectedCharacter.healthLevel
Entry(
date: currentDate,
character: selectedCharacter,
relevance: relevance
)
关键点:
relevance的取值范围由开发者自己定义。- session 选择直接使用角色生命值,因为完全恢复是这个 widget 最重要的状态。
- relevance 是系统智能的提示,不是强制展示命令。
4. 用 SiriKit custom intent 做主屏幕配置
(09:56)timeline 完成后,widget 仍然只显示 Power Panda。演示接着用 SiriKit Intent Definition file 增加配置项,让用户在 Home Screen 上选择角色。
Intent Definition file
category: information view
parameter: hero
enum values: Panda, Egghead, Spouty
StaticConfiguration -> IntentConfiguration
TimelineProvider -> IntentTimelineProvider
intent: CharacterSelectionIntent
snapshot(configuration)
timeline(configuration)
关键点:
- Intent Definition file 需要同时加入 widget target 和 app target。
- intent category 在演示中设置为 information view。
hero参数是 enum,演示里补齐 Panda、Egghead 和 Spouty。- widget 类型从
StaticConfiguration改为IntentConfiguration。 - provider 从 timeline provider 改为 intent timeline provider。
- snapshot 和 timeline 方法会多收到一个
configuration参数,provider 再把配置映射到 App 里的角色定义。
5. 点击 widget 回到对应内容
(02:54)Widget 没有动画或自定义交互,但可以 deep link 到 App。systemSmall 是一个完整点击区域;systemMedium 和 systemLarge 可以用 SwiftUI Link 创建多个可点击区域。
(13:42)演示在配置完成后给视图加 widgetURL modifier。这样用户点击当前角色的 widget,会直接进入对应角色详情页。
AvatarView(character: selectedCharacter)
.widgetURL(selectedCharacter.detailURL)
Link(destination: selectedCharacter.detailURL) {
AvatarView(character: selectedCharacter)
}
关键点:
widgetURL用在 small 和 medium 视图上,让点击进入当前角色。- 用户切换 favorite Ranger 后,跳转目标也跟着配置变化。
- medium 和 large family 有空间放多个点击区域时,可以使用 SwiftUI
Link。
核心启发
1. 做一个恢复倒计时 widget
做什么:把游戏角色、健身恢复、学习冷却时间这类已知结束时间的状态放到主屏幕。
为什么值得做:session 的核心例子就是角色会随时间恢复,并且 App 知道完全恢复的 endDate。
怎么开始:在 timeline provider 中从当前时间开始,每分钟生成一条 entry,直到 endDate,policy 先用 .atEnd。
2. 为同一内容设计 small 和 medium 两套读法
做什么:small 只显示一个关键状态,medium 增加一段补充信息。
为什么值得做:transcript 演示了 small 继续用 AvatarView,medium 用 HStack 加上角色 bio。
怎么开始:读取 widgetFamily environment value,用 switch 给不同 family 返回不同布局,再同时更新 primary view 和 placeholder。
3. 给用户一个主屏幕配置入口
做什么:让用户长按 widget 后选择要展示的角色、项目或账号。
为什么值得做:session 通过 CharacterSelectionIntent 让用户在 Edit Widget 里选择 Power Panda、Egghead 或 Spouty。
怎么开始:创建 Intent Definition file,把 category 设为 information view,添加 enum 参数,然后把 widget 改成 IntentConfiguration。
4. 给 Smart Stack 提供排序信号
做什么:在状态最重要的时间点提高 widget 的 relevance。
为什么值得做:session 明确说 widget 位于 stack 中时,系统可以根据 relevance 智能轮换。
怎么开始:选一个能代表优先级的数值。Emoji Rangers 用 health level,因为完全恢复是最重要的状态。
5. 让点击动作进入正确详情页
做什么:用户点 widget 后直接进入当前配置对应的详情页。
为什么值得做:session 演示配置成 Spouty 后,点击 widget 会进入 Spouty 的信息页。
怎么开始:给 small/medium 视图添加 widgetURL。如果 medium 或 large 中有多个目标区域,用 SwiftUI Link 拆出局部点击区域。
关联 Session
- Widgets Code-along, part 1: The adventure begins — 先完成 widget target、provider、placeholder 和 systemSmall 基础链路。
- Widgets Code-along, part 3: Advancing timelines — 本场结尾推荐的下一集,继续推进高级 timeline、URL 和多个 widget。
- Add configuration and intelligence to your widgets — transcript 在 custom intent 处推荐,用来深入 widget 配置和系统智能。
- Design great widgets — transcript 结尾推荐,用来判断 widget 的信息层级和视觉表达。
- Build SwiftUI views for widgets — 补足 SwiftUI widget 视图构建细节,适合和本场的 family 适配一起看。
评论
GitHub Issues · utterances