Complications and widgets: Reloaded
Swift & UI 进阶 20m

复杂功能与小组件:重载上阵

Complications and widgets: Reloaded

2022年6月6日

在 Apple 官方观看视频

一句话判断

iOS 16 锁屏小组件和 watchOS 9 复杂功能统一到 WidgetKit 框架——写一次代码,两个平台共享基础设施,accessory 家族让小组件出现在全新场景。

这场 Session 讲了什么

watchOS 团队的 Devon 和 iOS 团队的 Graham 介绍了 WidgetKit 在 iOS 16 和 watchOS 9 上的重大扩展。核心变化是:watchOS 的复杂功能(Complications)现在基于 WidgetKit 构建,而 iOS 16 新增了锁屏上的 accessory 小组件。

新增了三个 WidgetFamily:accessoryRectangular(多行文本或小图表)、accessoryCircular(简要信息、仪表盘、进度视图)、accessoryInline(单行文本)。这些家族在 iOS 锁屏和 watchOS 表盘上通用,代码只需写一次。

值得深挖的点

从 ClockKit 到 WidgetKit 的统一。watchOS 2 引入 ClockKit,watchOS 5 引入富复杂功能,watchOS 7 引入 SwiftUI 复杂功能。现在 WidgetKit 统一了所有这些。ClockKit 的 graphicCircular、graphicRectangular 等家族直接对应到新的 accessory 家族。

Accessory 家族的设计约束。这些小组件尺寸很小,信息密度要极高。accessoryCircular 适合展示单个数据点(温度、步数)加一个 gauge。accessoryRectangular 适合多行文本或小型折线图。accessoryInline 只能显示单行文本加一个图标。

颜色系统适配。accessory 小组件需要适配不同的显示环境。iOS 锁屏有浅色和深色模式,watchOS 表盘颜色各异。WidgetKit 提供了相应的颜色适配 API,确保内容在各种背景下都清晰可读。

隐私环境考量。锁屏是用户最常看到的界面,但也是最容易被他人在身边看到内容的场景。开发者需要考虑在不同隐私级别下展示多少信息——是否显示具体内容还是只显示未读数量。

代码片段

// 定义支持 accessory 家族的 Widget
@main
struct MyAppWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "MyAppWidget", provider: Provider()) { entry in
            MyAppWidgetEntryView(entry: entry)
        }
        .supportedFamilies([
            .accessoryCircular,
            .accessoryRectangular,
            .accessoryInline
        ])
    }
}

// 不同家族的视图适配
struct MyAppWidgetEntryView: View {
    let entry: SimpleEntry
    @Environment(\.widgetFamily) var family
    
    var body: some View {
        switch family {
        case .accessoryCircular:
            // 圆形:进度环 + 中心数字
            Gauge(value: entry.progress) {
                Text("\(entry.count)")
            }
            .gaugeStyle(.accessoryCircularCapacity)
            
        case .accessoryRectangular:
            // 矩形:多行文本
            VStack(alignment: .leading) {
                Text(entry.title)
                Text(entry.subtitle)
                    .font(.caption2)
            }
            
        case .accessoryInline:
            // 单行:图标 + 文本
            HStack {
                Image(systemName: "heart.fill")
                Text("心率: \(entry.heartRate) bpm")
            }
            
        default:
            Text(entry.title)
        }
    }
}

最佳实践

  • 使用 @Environment(\.widgetFamily) 为不同尺寸提供适配视图
  • accessory 小组件要极度精简——只展示最重要的单一数据点
  • 考虑隐私场景:锁屏上可能不适合展示消息内容,改为显示”3条未读”
  • watchOS 和 iOS 共享同一个 Widget Extension,善用代码复用
  • 颜色使用系统提供的 semantic colors,确保在浅色/深色表盘上都清晰

还有什么值得关注

  • 现有的 Home Screen Widget 代码可以扩展支持 accessory 家族,不需要新建 Extension
  • ClockKit 的复杂功能迁移到 WidgetKit 是推荐方向
  • watchOS 9 允许在表盘上放置多个来自同一应用的复杂功能
  • iOS 16 锁屏支持多个 accessory 小组件并排显示
WWDC 2022