Build complications in SwiftUI
watchOS 进阶 22m

用 SwiftUI 构建复杂功能

Build complications in SwiftUI

2020年6月23日

在 Apple 官方观看视频

一句话判断

这场 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(仪表盘)组件
WWDC 2020