小组件是什么
iOS 小组件是放在桌面、锁屏上的小卡片,用 WidgetKit 写,用 SwiftUI 画。它不常驻运行,按系统调度定时刷新,用来展示一眼能看完的信息。
小组件是给用户看一眼的小窗口,不是缩小版 App。
这篇把一个最简单的小组件跑起来。
小组件和主 App 是两个进程,不能直接共享内存。它们之间靠 App Group 共享容器传数据:主 App 写一份文件或 UserDefaults,小组件读这份。这也是为什么小组件总是"数据稍微慢半拍"——它等主 App 通知刷新才去读最新值。设计时要接受这一点,把小组件当成"展示当前状态"的窗口,而不是实时交互的控制台。
新建 Widget Target
在 Xcode 里 New Target,选 Widget Extension,它会生成一个独立 target,跟主 App 共享一份代码。
- TimelineProvider告诉系统画什么。
- WidgetEntryViewSwiftUI 画卡片。
- family指定小/中/大尺寸。
同一组尺寸可以共用一个视图,用 @Environment 拿尺寸做适配。深色模式靠系统自动切换,别自己写两套颜色。文字别用太小的,桌面一眼要看清,图标也是。
写视图与时间线
struct Provider: TimelineProvider {
func snapshot(in context: Context) async -> Entry { Entry(date: .now, temp: 26) }
func timeline(in context: Context) async -> Timeline<Entry> {
Timeline(entries: [Entry(date: .now, temp: 26)], policy: .after(.now.addingTimeInterval(1800)))
}
}
Timeline 的 policy 决定系统下次什么时候再来问你。.after 是定时刷新,.atEnd 是到时间线末尾再问一次。频率不能太频繁——系统对一天刷新多少次有上限,把不重要的更新攒一攒,一次刷完。
数据怎么更新
小组件自己不能一直跑。主 App 拉到新数据后存进共享容器,再告诉系统刷新小组件。系统按 policy 决定下次刷新时间。
主动刷新用 WidgetCenter.shared.reloadAllTimelines(),告诉系统"我数据变了,重画一次"。但别一有变化就调,系统会节流;把刷新合并到真正有意义的时刻,比如用户在主 App 做完一次操作之后。
点一下跳主 App
用 widgetURL 或 Link 包一层,点小组件就打开主 App 对应页面。
实战:天气小组件
Provider 每半小时给一组温度,EntryView 画大数字加图标,点一下跳到主 App 的详情。一个天气小组件就成了。
小结
WidgetKit 三件事:TimelineProvider 供数据、EntryView 画卡片、主 App 触发刷新。保持简单、一眼可读,就是好小组件。