Highlight
watchOS 7 为 Graphic Corner、Circular、Rectangular 和 Extra Large Complication 增加可承载 SwiftUI 视图的 ClockKit 模板,并提供 Text 自动更新、ProgressView、Gauge、表盘 tint 处理和 Xcode previews 支持。
核心内容
Apple Watch 的 Complication 负责在表盘上显示及时、相关的信息。Session 开头用 UVI complication 举例:一个很小的区域,可以用带颜色渐变的垂直 gauge 告诉用户今天是否需要防晒(00:22)。
难点在于,好的 Complication 要把复杂信息压缩到抬腕一眼能看懂的形态。过去主要依赖 ClockKit 预定义 template 和 provider;watchOS 7 增加了可以接收 SwiftUI view 的 template,覆盖 Graphic Corner、Circular、Rectangular 和 Extra Large families(01:29)。
SwiftUI 在这里给开发者一个更自由的画布。Session 特别提到 GraphicRectangularFullView 是新的 template,给 Rectangular family 提供更大的绘制区域。Dawn Patrol 的潮汐图和 Coffee Tracker 的周咖啡摄入图,都是把图表类信息放到表盘上的例子(02:03、13:48)。
这个自由度也有边界。Complication 是 timeline 里的静态 view,点击任意位置都会打开 App;按钮、手势、其他交互元素和 SwiftUI animation 都不支持。系统还会在 view 显示前测量性能,复杂或尺寸不合适的 view 会触发 runtime warning(19:12)。
详细内容
把 SwiftUI view 放进 ClockKit template
(01:29)ClockKit 新增的 Graphic templates 可以接收 SwiftUI view。下面这个官方片段同时展示了 SwiftUI view、相对时间文本,以及用 previewContext() 在 Xcode 里预览 complication。
import SwiftUI
import ClockKit
struct RelativeText: View {
var body: some View {
VStack(alignment: .leading) {
Text("Count Down")
.font(.headline)
.foregroundColor(.accentColor)
Label("Nap Time", systemImage: "moon.fill")
Text(Date() + 100, style: .relative)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct RelativeText_Previews: PreviewProvider {
static var previews: some View {
CLKComplicationTemplateGraphicRectangularFullView(RelativeText())
.previewContext()
}
}
关键点:
import SwiftUI和import ClockKit同时出现,previewContext()才能把 template 包成 SwiftUI preview 可识别的 view。RelativeText只是普通 SwiftUIView,可以用VStack、Text、Label和 modifier 组织内容。Text(Date() + 100, style: .relative)使用 session 点名的 relative date formatter,表盘会自动保持相对时间准确。CLKComplicationTemplateGraphicRectangularFullView(RelativeText())把 SwiftUI view 交给 Graphic Rectangular Full View template。.previewContext()让 Xcode previews 把这个 template 放到适合该 family 的表盘上。
用 Text、ProgressView 和 Gauge 表达一眼可读的信息
(02:38)SwiftUI 的 Text 会感知所在 complication family:默认字号会随 family 改变,默认字体也改为 SF Rounded。Session 还点名 relative、offset 和 timer styles,它们会由表盘自动更新。
import SwiftUI
import ClockKit
struct TimerText: View {
var body: some View {
VStack(alignment: .leading) {
Label("Sourdough Timer", systemImage: "timer")
.foregroundColor(.orange)
Text("Time remaining: \(Date() + 100, style: .timer)")
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct TimerText_Previews: PreviewProvider {
static var previews: some View {
CLKComplicationTemplateGraphicRectangularFullView(TimerText())
.previewContext()
}
}
关键点:
Label("Sourdough Timer", systemImage: "timer")用 SF Symbol 和短文字说明这个 complication 的用途。.foregroundColor(.orange)给标题一个明确状态色;后面的 tinting 规则会决定它在表盘着色时如何变化。Text("Time remaining: \(Date() + 100, style: .timer)")把 timer style 放进字符串插值,表盘会显示剩余时间。.frame(maxWidth: .infinity, alignment: .leading)让内容占满 Rectangular template 的可用宽度,并靠左排列。
(03:37)ProgressView 适合线性推进的信息,例如音乐进度;Gauge 适合随时间变化的值,例如温度或酸碱度。
import SwiftUI
import ClockKit
struct ProgressSample: View {
var body: some View {
VStack(alignment: .leading) {
Text("Water Reminder")
.foregroundColor(.blue)
Text("32 oz. consumed")
ProgressView(value: 0.7)
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
}
}
}
struct ProgressSample_Previews: PreviewProvider {
static var previews: some View {
CLKComplicationTemplateGraphicRectangularFullView(ProgressSample())
.previewContext()
}
}
关键点:
Text("Water Reminder")给用户一个标题,避免只有进度条时缺少语境。Text("32 oz. consumed")把当前状态写成短句,适合 Rectangular family 的横向空间。ProgressView(value: 0.7)表示一个 0 到 1 的进度值。LinearProgressViewStyle(tint: .blue)使用 session 中展示的线性样式和 tint。- Preview 仍然通过
CLKComplicationTemplateGraphicRectangularFullView和previewContext()完成。
import SwiftUI
import ClockKit
struct GaugeSample: View {
@State var acidity = 5.8
var body: some View {
Gauge(value: acidity, in: 3...10) {
Image(systemName: "drop.fill")
.foregroundColor(.green)
} currentValueLabel: {
Text("\(acidity, specifier: "%.1f")")
} minimumValueLabel: {
Text("3")
} maximumValueLabel: {
Text("10")
}
.gaugeStyle(
CircularGaugeStyle(
tint: Gradient(colors: [.orange, .yellow, .green, .blue, .purple])
)
)
}
}
struct GaugeSample_Previews: PreviewProvider {
static var previews: some View {
CLKComplicationTemplateGraphicCircularView(GaugeSample())
.previewContext()
}
}
关键点:
@State var acidity = 5.8给 Gauge 一个当前值,示例来自 session 的 garden soil acidity 场景。Gauge(value: acidity, in: 3...10)定义数值范围,session 中的范围是 3 到 10。currentValueLabel在 Gauge 中央显示当前值,帮助用户快速读数。minimumValueLabel和maximumValueLabel给起止值加标签。CircularGaugeStyle(tint: Gradient(...))使用 gradient tint,让不同区间有不同颜色提示。CLKComplicationTemplateGraphicCircularView对应 Circular family 的预览。
处理表盘 tint
(06:03)表盘支持多种 tint color,Complication 会跟随表盘颜色变化。Session 把 tinting 分成两种效果:desaturated tint 和 color opacity tint。
默认行为是 desaturated tint。表盘着色时,系统会创建 view 的灰度版本;部分表盘还会在灰度 view 上叠加单一颜色。Session 用一个黄色苹果和蓝色圆形说明:如果两个颜色亮度接近,desaturated 后苹果可能会消失,所以设计时要检查灰度状态(07:03)。
color opacity tint 需要开发者把 view hierarchy 分成两层。Transcript 点名的关键 API 是 complicationForeground modifier:默认内容位于 background layer,应用该 modifier 的内容会进入 foreground layer。表盘随后只看每层的 opacity,并由具体表盘决定给哪一层上色(08:32、10:12)。
更细的控制来自 ComplicationRenderingMode。它有 fullColor 和 tinted 两个值,可以从 environment 读取。Session 的例子是在 full-color 模式保留蓝色 fill,在 tinted 模式改成只改变 opacity 的 linear gradient,因为 color opacity tint 最终只使用 opacity(11:32)。
关键点:
- 默认 tinting 是 desaturated tint,先检查 view 在灰度下是否仍然可读。
complicationForeground用来把 view 的一部分提升到 foreground layer。- color opacity tint 只使用 opacity,原始颜色不会直接保留。
- 不同表盘可能给 foreground 和 background layer 应用不同颜色。
ComplicationRenderingMode允许同一个 view 针对fullColor和tinted分支调整渲染。
用 Xcode previews 检查复杂 complication
(13:42)Coffee Tracker 示例把 App 内的 HistoryView 和新的 ComplicationHistoryView 放在 preview 里并排查看,目的是避免改 complication 时误伤 App 内页面。复杂图表可以单独拆成 complication 专用 view,让表盘尺寸、字体和 tinting 调整集中在一个地方。
previewContext() 是这段工作流的入口。它定义在 CLKComplicationTemplate 上,并且需要同时 import ClockKit 和 SwiftUI。它会把 template 包装成 SwiftUI view,让 previews 把 complication 放到适合该 family 的表盘上(15:11)。
Session 还展示了 PreviewFaceColors:可以枚举不同表盘颜色,把 multicolor face 和 tinted face 放在一起看。这样能在修改另一个文件时实时观察 complication 如何随 tint 更新(18:16)。
关键点:
- 简单 view 可能只需要少量属性调整就能进入 complication。
- 复杂图表适合拆出单独的 complication view。
previewContext()让 template 进入 SwiftUI previews。PreviewFaceColors用来同时检查多个表盘颜色。- 预览要覆盖 full color 和 tinted 两种状态。
发布前检查限制和布局边界
(19:12)Complication 不是一个迷你 App。点击任何区域都会打开 App;Buttons、gestures 和其他 interactive elements 不受支持。Session 建议只使用 text、images 和 drawing primitives。
SwiftUI animation 也不受支持,因为 Complication 是由静态 view 组成的 timeline。系统会在显示前测量每个 SwiftUI view 的性能。Session 直接建议:如果 Xcode 对尺寸或复杂度给出 runtime warning,要把它当成 build error 修掉再发布(19:38)。
布局上,Circular 和 Rectangular families 会把内容 mask 到对应形状。Rectangular Full View 有 safe area,防止内容在某些表盘位置被 Apple Watch 屏幕边角裁掉。如果确实要使用完整区域,可以用 edgesIgnoringSafeArea,但要确认内容没有被裁切(20:46)。
关键点:
- Complication tap 统一打开 App,不能依赖局部按钮或手势。
- Timeline view 是静态内容,SwiftUI animation 不会在 complication 中工作。
- 图片尺寸、blur、formatted text 等绘制成本会影响运行时表现。
- Runtime warning 要在发布前修掉。
- Circular 和 Rectangular families 会 mask 内容;Full View 的 safe area 默认保护所有位置。
核心启发
- 做一个倒计时类 Complication:例如午睡、烘焙、会议前提醒。Session 的
Text(Date() + 100, style: .relative)和 timer style 会由表盘自动更新,起步方式是用CLKComplicationTemplateGraphicRectangularFullView承载一个短标题、SF Symbol 和时间文本。 - 做一个进度类 Complication:例如饮水量、音乐播放、任务完成度。ProgressView 的 circular 和 linear styles 已经适配小尺寸表盘,起步方式是先用
ProgressView(value:)表示 0 到 1 的状态,再按 family 选择 circular 或 linear style。 - 做一个区间读数 Complication:例如土壤酸碱度、紫外线、温度。Gauge 支持当前值、最小值、最大值和 gradient tint,起步方式是先确定数值范围,再用
Gauge(value:in:)加currentValueLabel让用户一眼看到读数。 - 把复杂图表拆成 complication 专用 view:Coffee Tracker 示例把周咖啡摄入图抽成 ComplicationHistoryView。起步方式是把 App 内的大图表缩成只保留标题和关键柱状或 gauge 的 SwiftUI view,然后用
previewContext()同时看 App view 和 complication view。 - 建立 tinting 预览清单:表盘 tint 会改变颜色和层级表现。起步方式是用
complicationForeground标记 foreground layer,用ComplicationRenderingMode分别处理fullColor和tinted,再用PreviewFaceColors检查多个表盘颜色。
关联 Session
- Create complications for Apple Watch — 从 ClockKit timeline、family 和 template 基础讲起,是本 session 的前置知识。
- Keep your complications up to date — 讲 timeline 更新、后台刷新和推送,让 SwiftUI complication 保持信息及时。
- What’s new in SwiftUI — 总览 2020 年 SwiftUI 的新控制、样式、widgets 和 custom complications。
- Meet Watch Face Sharing — 讲如何分享包含 App complications 的 watch face。
- Structure your app for SwiftUI previews — 深入 SwiftUI previews 的工程组织方式,可补足本 session 的预览工作流。
评论
GitHub Issues · utterances