为 Apple Watch 创建复杂功能
Create complications for Apple Watch
2020年6月23日
一句话判断
watchOS 7 彻底重构了 Complications 架构——从基于 CLKComplicationTemplate 的旧方案迁移到基于 SwiftUI 的新方案,如果你维护 Apple Watch App,这是一次不得不做的迁移。
这场 Session 讲了什么
Complications(复杂功能)是 Apple Watch 表盘上的小组件,显示来自不同 App 的关键信息。watchOS 7 引入了全新的 Complication 架构,最核心的变化是用 SwiftUI 构建表盘内容,替代了旧版的 ClockKit Template 系统。
新的架构围绕 CLKComplicationDescriptor(描述你的 Complication 的标识和支持的样式)、CLKComplicationTimelineProvider(提供时间线数据)和 SwiftUI 视图(渲染 Complication 内容)三个部分。每个 Complication 现在可以有多个族的变体,用户可以在表盘上选择不同的数据展示方式。
Session 还介绍了 Graphic Complications 的新样式——watchOS 7 的全新表盘(如 Chronograph Pro 和 X-Large)支持更丰富的图形化 Complications,包括环形进度条、角标、仪表盘等。
值得深挖的点
从 Template 到 SwiftUI 的范式转变。 旧版 Complications 使用预定义的 Template(如 CLKComplicationTemplateCircularSmallRingText),只能调整文字和颜色。新的 SwiftUI 方案让你可以自由设计 Complication 的外观,但代价是需要自己处理布局适配——表盘空间非常有限,每个像素都要精打细算。
多条 Complications 支持。 watchOS 7 允许一个 App 提供多个 Complication Descriptor。比如天气 App 可以提供”当前温度”、“未来降雨概率”两个独立的 Complication,用户在同一个表盘上可以同时添加这两个。
代码片段
import ClockKit
import SwiftUI
// 定义 Complication Descriptor
let descriptors: [CLKComplicationDescriptor] = [
CLKComplicationDescriptor(
identifier: "currentWeather", // 唯一标识
displayName: "当前天气",
supportedFamilies: CLKComplicationFamily.allCases
),
CLKComplicationDescriptor(
identifier: "rainProbability",
displayName: "降雨概率",
supportedFamilies: [.graphicCircular, .graphicCorner]
)
]
// Timeline Provider
struct WeatherTimelineProvider: CLKComplicationTimelineProvider {
func currentTimelineEntry(
for complication: CLKComplication,
with date: Date
) -> CLKComplicationTimelineEntry? {
let weather = fetchWeather(for: date)
return CLKComplicationTimelineEntry(
date: date,
complicationTemplate: createTemplate(for: complication, weather: weather)
)
}
func timelineEntries(
for complication: CLKComplication,
after date: Date,
limit: Int
) -> [CLKComplicationTimelineEntry]? {
// 提供未来的时间线条目
let entries = (1...limit).compactMap { hour -> CLKComplicationTimelineEntry? in
let futureDate = Calendar.current.date(byAdding: .hour, value: hour, to: date)!
let weather = fetchWeather(for: futureDate)
return CLKComplicationTimelineEntry(
date: futureDate,
complicationTemplate: createTemplate(for: complication, weather: weather)
)
}
return entries
}
}
// Graphic Circular 样式的 Complication 模板
func createTemplate(for complication: CLKComplication, weather: Weather) -> CLKComplicationTemplate {
switch complication.family {
case .graphicCircular:
let template = CLKComplicationTemplateGraphicCircular(
text: Text("\(Int(weather.temperature))°")
)
return template
case .graphicCorner:
let template = CLKComplicationTemplateGraphicCorner(
text: Text("\(weather.condition)")
)
return template
default:
let template = CLKComplicationTemplateExtraLargeColumns(
leadingColumn: .init(format: "\(Int(weather.temperature))"),
trailingColumn: .init(format: "°C")
)
return template
}
}
最佳实践
- 提供多个 Complication Descriptor。 让用户选择不同的数据维度展示在表盘上,增加 App 的可见度。
- Complication 数据要精简。 表盘空间极小,只展示最关键的一两个数字或简短文字。
- 预生成充足的时间线数据。 Complication 不会频繁刷新,提供未来几小时的数据让系统在合适时机更新。
- 测试所有表盘族的适配。 不同的表盘位置(圆形、矩形、角标等)需要不同的模板。
还有什么值得关注
- watchOS 7 的共享表盘功能让用户可以分享包含自定义 Complication 的表盘配置
- Complication 的刷新预算与 WidgetKit 类似,不要频繁请求刷新
- 新的 Chronograph Pro 和 X-Large 表盘提供了更大的 Complication 展示区域
- Complication 支持彩色渲染,可以在 SwiftUI 中使用彩色图标