用 SwiftUI 构建复杂功能
Build complications in SwiftUI
2020年6月23日
一句话判断
这场 Session 是 10046 的 SwiftUI 深入版——展示如何用 SwiftUI 视图直接渲染 Complication 内容,而不是使用 ClockKit 的 Template 系统,这是 watchOS 7 Complication 开发的推荐方式。
这场 Session 讲了什么
watchOS 7 的 Complication 可以用 SwiftUI 视图来渲染内容,这是最重大的架构变化。过去你只能从预定义的 Template 中选择,填充文字和颜色。现在你可以在 SwiftUI 中自由设计 Complication 的外观。
Session 重点介绍了 Graphic 家族的 Complication——它们支持 SwiftUI 视图,包括 CLKComplicationTemplateGraphicCircular(view:)、CLKComplicationTemplateGraphicRectangularFull(view:) 等新的初始化方法。
核心设计原则:Complication 的渲染空间极小(圆形只有约 40pt 直径),SwiftUI 视图必须极简。Session 建议使用系统 SF Symbols 和简短文字,避免复杂的布局。系统会自动处理深色模式适配。
值得深挖的点
SwiftUI 视图在 Complication 中的限制。 Complication 的 SwiftUI 视图不支持动画、手势、NavigationLink 等交互功能。它的渲染机制类似 Widget——系统将 SwiftUI 视图渲染为静态图片显示在表盘上。所以你写的是”描述式 UI”而非”交互式 UI”。
** tint color 的适配。** 表盘会提供一个 tint color,你的 Complication 应该适配这个颜色。使用 \.complicationTint 环境值来获取当前表盘的色调,确保你的 Complication 在任何表盘上看起来都协调。
代码片段
import ClockKit
import SwiftUI
// Graphic Circular Complication 用 SwiftUI 渲染
struct CircularWeatherView: View {
var temperature: Int
@Environment(\.complicationTint) var tint
var body: some View {
ZStack {
// 环形进度条表示温度范围
ProgressView(value: Double(temperature), total: 40.0)
.tint(tint)
VStack(spacing: 0) {
Text("\(temperature)")
.font(.system(size: 18, weight: .bold, design: .rounded))
.foregroundColor(tint)
Text("°C")
.font(.system(size: 10))
.foregroundColor(tint.opacity(0.7))
}
}
}
}
// 创建 Graphic Circular 模板
let template = CLKComplicationTemplateGraphicCircularView(
CircularWeatherView(temperature: 22)
)
// Graphic Rectangular Complication——更大的展示空间
struct RectangularWorkoutView: View {
var workout: WorkoutData
@Environment(\.complicationTint) var tint
var body: some View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(workout.type.rawValue)
.font(.system(size: 12, weight: .semibold))
.foregroundColor(tint)
Text("\(workout.calories) 千卡")
.font(.system(size: 16, weight: .bold, design: .rounded))
Text("\(workout.duration) 分钟")
.font(.system(size: 11))
.foregroundColor(.secondary)
}
Spacer()
// 活动环缩略图
Image(systemName: "flame.fill")
.foregroundColor(tint)
.font(.title3)
}
.padding(4)
}
}
// 在 Timeline Entry 中使用 SwiftUI 模板
func currentTimelineEntry(
for complication: CLKComplication,
with date: Date
) -> CLKComplicationTimelineEntry? {
let weather = fetchWeather(for: date)
let template: CLKComplicationTemplate
switch complication.family {
case .graphicCircular:
template = CLKComplicationTemplateGraphicCircularView(
CircularWeatherView(temperature: weather.temperature)
)
case .graphicRectangularFull:
template = CLKComplicationTemplateGraphicRectangularFullView(
RectangularWorkoutView(workout: currentWorkout)
)
default:
return nil
}
return CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
}
最佳实践
- Complication 视图控制在 3-4 个 SwiftUI 元素以内。 空间极其有限,每个像素都很珍贵。
- 使用
complicationTint环境值适配表盘色调。 确保你的 Complication 在不同颜色的表盘上都能协调。 - 优先使用 SF Symbols 而非自定义图片。 SF Symbols 在任何尺寸下都清晰,且自动适配 tint color。
- 测试所有 Graphic 家族的尺寸。 Circular、Corner、Rectangular 等样式空间差异很大,需要分别优化。
还有什么值得关注
- SwiftUI Complication 只支持 Graphic 家族,传统的 Utilitarian 和 Modular 家族仍然使用 Template
- Complication 的渲染分辨率适配各种表盘大小
- watchOS 7 允许在同一个表盘上放置同一个 App 的多个 Complication
- Complication 的 SwiftUI 视图支持 Gauge(仪表盘)组件