Highlight
iOS 15 为 WidgetKit 带来可配置性、隐私敏感度控制、以及针对不同环境和位置的适配能力,同时更新了设计原则确保小组件保持相关性和个性化。
核心内容
Widget 的核心价值是”一瞥即得”。用户不需要打开 App,就能在主屏幕、锁屏(iOS 16 起)或智能堆叠中获取关键信息。iOS 15 在这个基础上增加了三件事:让用户自定义 Widget 内容、保护敏感信息、以及让 Widget 适应不同的显示环境。
可配置性意味着用户可以决定 Widget 显示什么内容。股票 Widget 可以选择关注哪些股票,天气 Widget 可以选择关注哪些城市。iOS 15 引入了 IntentConfiguration,让用户通过长按 Widget 打开配置界面,选择自己关心的内容。
隐私敏感度控制解决的是”主屏幕上不应该显示私密信息”的问题。用户锁屏后,银行卡余额应该被遮挡。iOS 15 提供 .privacySensitive() 修饰符,系统会在锁屏或隐私模式下自动用占位符替代敏感内容。
环境适配指的是同一个 Widget 在不同位置可能有不同的呈现方式。主屏幕上的天气 Widget 可以显示详细温度,而在智能堆叠中可能只需要一个图标。iOS 15 为 Widget 提供了 widgetFamily 和 isPreview 等环境值,让开发者根据上下文调整布局。
详细内容
Xcode Previews 支持暗色模式
(15:46)Widget 的 Xcode Previews 现在支持显式设置颜色方案,方便同时预览亮色和暗色效果。
struct MyWidget_Previews: PreviewProvider {
static var previews: some View {
MyWidgetEntryView(date: Date())
.previewContext(WidgetPreviewContext(family: .systemSmall))
.environment(\.colorScheme, .dark)
}
}
关键点:
WidgetPreviewContext指定 Widget 的尺寸系列.environment(\.colorScheme, .dark)切换到暗色模式预览- 可以创建多个 preview 实例同时展示不同尺寸和颜色方案
隐私敏感度控制
(16:34)金融、健康等类别的 Widget 常常需要显示敏感数据。用户锁屏后,这些信息应该被遮挡。
struct MyWidgetEntryView: View {
var body: some View {
ZStack {
Rectangle().fill(BackgroundStyle())
VStack(alignment: .leading) {
Text("Balance")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.blue)
Text("$128.45")
.privacySensitive()
.font(.title2)
.foregroundColor(Color.gray)
}
}
}
}
关键点:
.privacySensitive()标记可能包含敏感信息的视图- 系统会在锁屏、隐私模式等环境下自动用占位符替代
- 占位符通常是圆点、方块或通用图标
- 不需要手动判断当前是否处于隐私模式
WidgetBundle 组织多个 Widget
(23:08)一个 App 可以提供多个 Widget,通过 WidgetBundle 统一管理。
struct IndividualSymbolWidget: Widget {
var body: some WidgetConfiguration {
// 配置
}
}
struct StocksOverviewWidget: Widget {
var body: some WidgetConfiguration {
// 配置
}
}
@main
struct MyWidgetBundle: WidgetBundle {
var body: some Widget {
// 这里的顺序决定了 Widget Gallery 中的显示顺序
IndividualSymbolWidget()
StocksOverviewWidget()
}
}
关键点:
@main标记的 WidgetBundle 是 Widget 扩展的入口点- Bundle 中 Widget 的顺序与 Widget Gallery 中的一致
- 每个 Widget 可以有独立的配置和 TimelineProvider
静态配置与 Intent 配置
(25:43)Widget 有两种配置方式:静态配置和 Intent 配置。
// 静态配置 - 适合不需要用户自定义的 Widget
@main
public struct SampleWidget: Widget {
public var body: some WidgetConfiguration {
StaticConfiguration(
kind: "com.sample.myStaticSampleWidgetKind",
provider: Provider()
) { entry in
SampleWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
public struct Provider: TimelineProvider {
public func timeline(with context: Context,
completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Date())
completion(timeline)
}
}
// Intent 配置 - 适合需要用户自定义的 Widget
@main
public struct SampleWidget: Widget {
public var body: some WidgetConfiguration {
IntentConfiguration(
kind: "com.sample.myIntentSampleWidgetKind",
intent: SampleConfigurationIntent.self,
provider: Provider()
) { entry in
SampleWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
public struct Provider: IntentTimelineProvider {
public func timeline(for configuration: SampleConfigurationIntent,
with context: Context,
completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(timeline)
}
}
关键点:
StaticConfiguration使用TimelineProvider,适合内容固定的 WidgetIntentConfiguration使用IntentTimelineProvider,需要传递用户配置的 Intent- Intent 类型需要在
.intentdefinition文件中定义 - 用户长按 Widget 可以打开配置界面,修改 Intent 参数
核心启发
-
多城市天气 Widget
- 做什么:天气 Widget 让用户选择关注的城市数量和排序
- 为什么值得做:
IntentConfiguration让每个用户都能定制自己的天气面板 - 怎么开始:在
.intentdefinition中定义CityConfigurationIntent,包含城市列表参数,Provider 根据用户选择的城市生成对应的 Timeline
-
个人财务仪表盘
- 做什么:银行余额 Widget 在锁屏时自动遮挡具体金额,只显示”有余额”或余额范围
- 为什么值得做:
.privacySensitive()一行代码就能保护敏感信息 - 怎么开始:在显示金额的 Text 视图上添加
.privacySensitive(),系统自动处理遮挡逻辑
-
智能堆叠自适应布局
- 做什么:新闻 Widget 在主屏幕显示标题摘要,在智能堆叠中只显示分类图标和头条标题
- 为什么值得做:
widgetFamily环境值可以判断当前尺寸,动态调整信息密度 - 怎么开始:用
@Environment(\.widgetFamily)获取当前尺寸,@Environment(\.isPreview)判断是否在预览中,根据环境返回不同的视图
-
多股票跟踪器
- 做什么:股票 Widget 让用户添加关注的多只股票,按用户指定的顺序显示
- 为什么值得做:Intent 支持数组参数,用户可以自由组合自己关心的股票
- 怎么开始:定义
StockSymbolsIntent包含[String]类型的股票代码数组,Provider 根据数组内容获取实时报价
-
分时段日程提醒
- 做什么:日历 Widget 根据时间段显示不同内容:早上显示今日行程,下午显示通勤路况,晚上显示明天的天气
- 为什么值得做:
TimelineEntry可以为不同时间点设置不同的内容 - 怎么开始:在 Timeline 中为不同的时间点生成不同的 Entry,每个 Entry 包含该时间段最相关的信息
关联 Session
- Widgets code-along — Widgets 入门实践和代码演示
- Build a document group app — SwiftUI App 结构与 Widget 配置
- Design for spatial interaction — 空间交互设计原则
评论
GitHub Issues · utterances