Highlight
watchOS 10 引入 Smart Stack,让 widget 可以直接覆盖在表盘上方。开发者通过六种标准布局、自定义配色和基于信号的 relevancy 机制,能让用户抬腕 10 秒内获取最关键的信息。
核心内容
从表盘到 Smart Stack:信息获取方式的进化
Apple Watch 的核心价值是抬腕即见(glanceable information)。以前,复杂功能(complications)只能嵌入特定表盘,像 Wayfinder、Modular 这类表盘虽然信息密度高,但牺牲了个性化表达的空间。
watchOS 10 的 Smart Stack 解决了这个矛盾。无论用户选择什么表盘,转动数码表冠就能呼出一个按相关性智能排序的 widget 堆栈。用户还可以手动添加、删除或置顶 widget,让堆栈既动态又可控。
(00:14)
六种标准布局:保持一致性的设计语言
widget 的阅读体验像翻书,每页的风格、位置、字号应该一致。Apple 为此定义了六种标准布局:
三行文本布局 — 适合纯文本信息。News widget 用它展示标题和摘要。
带颜色编码的文本布局 — 在文本基础上加入颜色区分。Calendar widget 用它同时显示事件内容和日历颜色。
条形进度图布局 — 图形内容配辅助文字。Audiobook widget 用它展示阅读进度和当前章节。
圆形图形布局 — 适合环形数据展示。Activity widget 的健身环就是这个布局,文字补充卡路里、运动时长等细节。
大文本布局 — 适合单个数值或关键词。Calendar Month widget 用它显示日期,你也可以用它展示状态词如”高”或”低”。
图表布局 — 适合时间序列数据。
(02:49)
设计 widget 时只展示必要信息。用户在 Watch 上浏览 glanceable 信息的时间不应超过 10 秒。
如果标准布局无法满足需求,可以自定义布局,但仍建议使用系统文本样式类保证可读性。
颜色与图标:让 widget 一眼可识别
Smart Stack 中的 widget 不再受表盘色调限制,开发者可以更自由地使用品牌色或信息色。
默认情况下 widget 使用深色材质背景配白色文字。Apple 鼓励开发者选择有辨识度的背景色:Weather widget 用动态渐变传达天气状况,Stocks widget 用红绿区分涨跌,Audiobook widget 用模糊的书封面营造氛围。
图标方面,建议使用 SF Symbol 或矢量图标,与文字搭配更协调。
(07:02)
Session 与 widget 的配合
Session 是应用中有明确起止时间的活跃状态,比如播放音乐、计时器运行、健身记录中。
Smart Stack 会自动在顶部生成系统级的 Session Control widget。因此,如果你的 widget 与 Session 相关(如音乐或健身),应该聚焦于 Session 前后的辅助信息,比如推荐一首歌或展示今日训练计划。这样既避免了功能重复,又能在 Session 期间保持 widget 的实用性。
(08:41)
Relevancy:让 widget 在最需要的时候出现
Smart Stack 的核心能力是按相关性智能排序。以下是五个值得利用的信号时刻:
- 时间和日期临近 — Calendar widget 在事件开始前一小时提升优先级
- 到达特定位置 — Reminders 用 GPS 或推断位置(家、公司、学校)触发
- 检测到耳机 — Audiobooks 在 AirPods 连接时置顶
- 起床或就寝时间 — 结合睡眠数据
- 开始或结束运动 — Activity 在有活跃 Workout 时提升排序
(10:01)
详细内容
Smart Stack 的交互模型
Smart Stack 的交互由两个核心行为驱动:
- 系统智能排序:基于时间、位置、设备状态等信号自动调整 widget 顺序
- 用户手动控制:添加、删除、置顶 widget
Combo widget 是每个 Smart Stack 的默认组件,支持三个圆形复杂功能。它适合放置圆形应用启动器,但更好的用法是让三个复杂功能形成信息组合,比如同时显示温度、紫外线指数和空气质量。
设计实践要点
widget 设计需要回答三个问题:
- 布局:内容适合哪种标准布局?是否需要自定义?
- 识别度:背景色和图标能否让用户一眼认出你的应用?
- 时机:什么信号能让你的 widget 在最需要的时候浮到顶部?
代码示例:WidgetKit 基础结构
虽然本 session 是设计导向,没有提供代码片段,但实现 Smart Stack widget 需要以下 WidgetKit 基础结构:
import WidgetKit
import SwiftUI
struct SmartStackWidget: Widget {
let kind: String = "SmartStackWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
SmartStackWidgetView(entry: entry)
}
.configurationDisplayName("My App Widget")
.description("Glanceable info for Smart Stack")
.supportedFamilies([.accessoryRectangular])
}
}
struct Provider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
// 根据 relevancy 信号生成时间线
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, relevance: nil)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let relevance: TimelineEntryRelevance?
}
关键点:
supportedFamilies设置为.accessoryRectangular,这是 Smart Stack 支持的 widget 尺寸TimelineEntryRelevance用于控制 widget 在堆栈中的排序优先级- Provider 的
getTimeline方法根据业务逻辑预生成多个时间点的内容
使用 Relevancy 提升 widget 优先级
import WidgetKit
struct RelevantProvider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<RelevantEntry>) -> ()) {
let now = Date()
// 模拟一个即将开始的事件
let eventStart = Calendar.current.date(byAdding: .minute, value: 30, to: now)!
let entry = RelevantEntry(
date: now,
relevance: TimelineEntryRelevance(
score: 100, // 分数越高,排序越靠前
duration: 3600 // 优先级持续 1 小时
)
)
let timeline = Timeline(entries: [entry], policy: .after(eventStart))
completion(timeline)
}
}
struct RelevantEntry: TimelineEntry {
let date: Date
let relevance: TimelineEntryRelevance?
}
关键点:
TimelineEntryRelevance的score决定 widget 在堆栈中的相对位置duration指定该优先级持续的时间窗口- 结合日历事件、位置变化或耳机连接等信号动态调整 score
核心启发
-
做一个”会议前提醒”widget
- 做什么:在会议开始前 15 分钟自动浮到 Smart Stack 顶部,显示会议室位置和参会人
- 为什么值得做:利用时间 relevancy 信号,解决”快到会议了才想起来看日程”的痛点
- 怎么开始:用
TimelineEntryRelevance(score:duration:)在事件前设置高优先级,widget 用三行文本布局展示会议标题、时间、地点
-
做一个”到家自动切换”widget
- 做什么:用户到达家附近时,widget 置顶显示智能家居快捷控制
- 为什么值得做:利用位置 relevancy,把”打开 Home app → 找到设备 → 操作”的三步流程压缩成抬腕一瞥
- 怎么开始:用
CLRegion定义家的地理围栏,在getTimeline中根据位置状态返回不同 relevance score
-
做一个”运动后数据”widget
- 做什么:Workout 结束后自动显示本次运动的摘要(时长、消耗、心率区间)
- 为什么值得做:与系统 Session Control widget 互补,在用户最需要回顾数据的时刻出现
- 怎么开始:监听
HKWorkoutSession状态变化,在 workout 结束时通过WidgetCenter.shared.reloadTimelines(ofKind:)触发 widget 刷新并设置高 relevance
-
做一个”耳机连接推荐”widget
- 做什么:检测到耳机连接时,置顶推荐最近没听完的播客或歌单
- 为什么值得做:Audiobooks 的同款思路,把”想听什么”的决策成本降到零
- 怎么开始:用
AVAudioSession监听路由变化,在耳机连接时通过 relevance 机制提升 widget 排序
关联 Session
- Build widgets for the Smart Stack on Apple Watch — 配套的代码实践 session,手把手教你实现 Smart Stack widget
- Bring widgets to new places — WidgetKit 跨平台扩展的完整指南
- Design dynamic Live Activities — 实时活动的设计理念与 widget 互补
- Meet SwiftUI for spatial computing — 了解 Apple 最新的 UI 框架设计理念
评论
GitHub Issues · utterances