iOS14 Widget仅支持SwiftUI,支持在任意主界面上进行Widget页面的编辑,UI变成了三种尺寸,每一个Widget都是一个单独的组件,他们可以自己管理自己通过简单的长按下压手势进行编辑和删除操作。
一、iOS14 创建Widget
创建iOS的项目:
- Create a new Xcode Project
- 语言选择:Swift/OC
创建Widget组件:
- File->New->Target添加Widget Extension Target 点击Next
- 输入Widget组件名,取消勾选Include Configuration Intent,点击Finish就可以了。
- 此时工程默认生成模板,可以看到Widget三个尺寸。
二、生命周期解读代码
1. Provider
- 为小组件展示提供一切必要信息的结构体,实现TimelineProvider协议
1 | struct Provider: TimelineProvider { |
- placeholder:提供一个默认的视图,当网络数据请求失败或者其他一些异常的时候,用于展示
- getSnapshot:为了在小部件库中显示小部件,WidgetKit要求提供者提供预览快照,在组件的添加页面可以看到效果
- getTimeline:在这个方法内可以进行网络请求,拿到的数据保存在对应的entry中,调用completion之后会到刷新小组件。
- 请求时间线有两个地方:一个是按照策略请求,一个在请求即时快照时。
- 刷新小组件并不能重新获取展示数据,只有重启时间线,才能重新获取数据。
- 如果需要定义小组件的刷新策略为每分钟刷新,15分钟后重启时间线:
1 | //设置时间线 |
- 关于重启策略,根据官方文档,每个配置的小部件每天接收有限的重启次数。有几个因素会影响小部件接收的重启次数,例如:包含的应用程序是在前台还是后台运行,小部件在屏幕上显示的频率,以及包含的应用程序参与的活动类型。
- 根据我参考其他开发者的分享,设定在5分钟以下的重启时间线策略几乎无效。一般设定在15分钟以上的策略,才能按时重启时间线。
策略有如下几种:
1 | //在结束时重启 |
2. SimpleEntry
- 实现TimelineEntry协议,就是用来保存所需要的数据。
- 其中TimelineEntry含有date属性。
- 可以继续添加其他的属性。例如自定义一个展示用的model:
1 | struct Model { |
3. 加载入口
- YourWidget是我们为组件设置的名字,模板自动使用这个名字帮我们生成了一个实现了Widget协议的结构体。
1 | struct YourWidget: Widget { |
- StaticConfiguration是系统提供的组件配置结构体,其用来对静态类型的组件提供配置。
- kind:是Widget的唯一标识
- StaticConfiguration:初始化配置代码
- configurationDisplayName:添加编辑界面展示的标题
- description:添加编辑界面展示的描述内容
- supportedFamilies这里可以限制要提供三个样式中的哪几个
1 | 一个Widget只提供了三个样式的选择:大、中、小 |
4. SwiftUI展示UI
1 | VStack:垂直排列元素 |
1 | 显示文本方法: |
1 | 显示图片方法: |
三、Widget与App交互及获取数据
1. 唤起APP跳转
- systemSmall只能用widgetURL修饰符实现URL传递:
1 | ZStack(content: { |
- systemMedium、systemLarge可以用Link或者 widgetUrl处理:
1 | Link(destination: URL(string: "abc://...")!){ |
2. 主App传值到Widget
先使用开发者账号创建主App与Widget的group,生成groupid;
通过NSUserDefault或NSFileManager进行通信;
- App存值:
1 | NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"groupId"]; |
- Widget取值:
1 | //从UserDefault中取值 |
3. 加载网络图片
无法异步加载图片,只能同步加载
1 | let Image:UIImage = { |
4. 网络请求
1 | static func request(completion: @escaping (Result<SimpleEntry, Error>) -> Void) { |
5. App主动刷新Widget
因为主动刷新小组件,仅支持swift。所以如果在OC原生环境中使用,需加入swift作为桥接。
创建 WidgetKitManager.swift文件
1 | import WidgetKit |
OC中使用刷新小组件:
1 | if (@available(iOS 14.0, *)) { |
四、用户属性配置
在第一次创建Widget的时候,有一个选项我们没有勾选,Include Configuration Intent。这选项主要是用来支持你自定义一些属性配置(例如天气组件,用户可以选择城市,股票组件,用户可以选择代码等)
1. 补充创建
菜单File ->New ->File然后找到Siri Intent Definition File之后添加到Widget中.
这里一定记得勾选。
添加Intent
将属性改为:
此时系统会根据你的命名生成一个只读文件:
2.设置可配置选项
这个选项设置完,就在上图中的代码上会有对应显示。
3.修改代码
1 | import Intents |
SimpleEntry添加ConfigurationIntent属性
1 | //从UserDefault中取值 |
TimelineProvider -> IntentTimelineProvider
1 | struct Provider: TimelineProvider { |
StaticConfiguration -> IntentConfiguration
1 | var body: some WidgetConfiguration { |
显示
1 | Text(entry.configuration.parameter == nil ? "没有值" : entry.configuration.parameter!) |
五、优化及调试
1. 优化
在widget使用数据之前,App应已经准备数据。使用共享组group存储数据。
让App使用后台处理时间来更新widget数据。
选择最合适的刷新策略。
仅当小部件当前显示的信息更改时,才调用reloadtimelines。
2. 调试
在Xcode中调试时,WidgetKit不会施加刷新次数限制。要验证Widget的行为是否正确,需使用真机测试。
- 本文作者: Grx
- 本文链接: https://ruixiaoguo.github.io/Grx.github.io/Grx.github.io/2021/12/23/iOS 14 Widget小组件 (SwiftUI)/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!