首先了解下如何创建
xcode -> file -> new -> target 找到 widget extension
如果你的 widget 支持用户配置属性,则需要勾选这个(例如天气组件,用户可以选择城市),不支持的话则不用勾选
了解下创建widget后,系统给我们生成的文件内容
下面这个代码是没有勾选 include configuration intent 的地方
provider
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// provider,顾名思义为小组件提供信息得一个struct
struct provider: timelineprovider {
public typealias entry = simpleentry
// 编辑屏幕时,左上角选择添加小组件时候,第一次展示小组件会走这个方法
public func snapshot(with context: context, completion: @escaping (simpleentry) -> ()) {
}
// 这个方法内可以进行网络请求,拿到的数据保存在对应的 entry 中,调用 completion 之后会到刷新小组件
public func timeline(with context: context, completion: @escaping (timeline<entry>) -> ()) {
// 例如这是一个网络请求
network.request { data in
let entry = simpleentry(date: renderdate, data: data)
let timeline = timeline(entries: [entry], policy: .after(nextrequestdate))
completion(timeline)
}
}
}
|
entry
官方解释: a type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget's content.
|
1
2
3
4
5
|
// 我的理解是就是存储小组件的数据的一个东西
struct simpleentry: timelineentry {
let date: date
let data: data
}
|
placehodlerview
|
1
2
3
4
|
// 这个是一个默认视图,例如网络请求失败、发生未知错误、第一次展示小组件都会展示这个view
struct placeholderview : view {
}
|
widgetentryview
|
1
2
3
4
|
// 这个是我们需要布局小组件长什么样子的view
struct staticwidgetentryview : view {
}
|
主入口
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@main
struct staticwidget: widget {
private let kind: string = "staticwidget"
public var body: some widgetconfiguration {
staticconfiguration(kind: kind, provider: provider(), placeholder: placeholderview()) { entry in
staticwidgetentryview(entry: entry)
}
.configurationdisplayname("my widget")
.description("this is an example widget.")
}
}
|
支持多widget样式
|
1
2
3
4
5
6
7
8
9
10
|
@main
struct mainwidgets: widgetbundle {
@widgetbundlebuilder
var body: some widget {
widget1()
widget2()
}
}
|
勾选 include configuration intent 之后可能出错的地方
如果你的app中设置了 class prefix 这下面这个 configurationintent.self 则需要加上对应的前缀
例如前缀是 xy 则需要修改为 xyconfigurationintent.self
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@main
struct mainwidget: widget {
private let kind: string = "mainwidget"
public var body: some widgetconfiguration {
intentconfiguration(kind: kind, intent: xyconfigurationintent.self, provider: provider(), placeholder: placeholderview()) { entry in
intentwidgetentryview(entry: entry)
}
.configurationdisplayname("my widget")
.description("this is an example widget.")
}
}
|
处理widget点击事件
widget 支持三种显示方式,分别是 systemsmall 、 systemmedium 、 systemlarge
small 样式只能用 widgeturl 处理
|
1
2
3
4
5
6
7
8
9
|
@viewbuilder
var body: some view {
zstack {
avatarview(entry.character)
.widgeturl(url)
.foregroundcolor(.white)
}
.background(color.gamebackground)
}
|
medium 和 large 可以用 link 或者 widgeturl 处理,我们看到里面有四个相同的view,即左边图片,右边文字的,这个view代码如下(link方式)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
struct recipeview: view {
let recipe: recipemodel
var body: some view {
link(destination: url(string: "你的网址")!) {
hstack {
webimageview(imageurl: recipe.squareimageurl)
.frame(width: 65, height: 65)
text(recipe.adjname + recipe.name)
.font(.footnote)
.bold()
.foregroundcolor(.black)
.linelimit(3)
}
}
}
}
|
添加 link 或 widgeturl 后,点击每个 recipeview 都会触发事件,这时候你需要在主项目中的 appdelegate 中的如下方法进行处理
|
1
2
3
|
func application(_ app: uiapplication, open url: url, options: [uiapplication.openurloptionskey : any] = [:]) -> bool {
}
|
关于widget中加载网络图片的时机
当我们在func timeline(withcompletion)这个方法中请求到数据拿到图片链接后,必须同步把图片解析出来,否则直接让对应的widgetview去load url 是加载不出来的
正确的写法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
struct model {
...
let image: uiimage
}
func timeline(with context: context, completion: @escaping (timeline<lfplanentry>) -> ()) {
network.request { data in
// 解析图片
var image: uiimage? = nil
if let imagedata = try? data(contentsof: url) {
image = uiimage(data: imagedata)
}
let model = model(image: image ?? defalutimage) // 这里给个默认图片
let entry = simpleentry(date: entrydate, data: model)
let timeline = timeline(entries: [entry], policy: .atend)
completion(timeline)
}
}
struct widgetview: view {
let model: model
@viewbuilder
var body: some view {
image(uiimage: model.image)
.resizable()
}
}
|
错误的写法(直接丢url给view去加载)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
struct widgetview : view {
let model: model
@state private var remoteimage : uiimage? = nil
let defaultimage = uiimage(named: "default")!
var body: some view {
image(uiimage: self.remoteimage ?? defaultimage)
.onappear(perform: fetchremoteimage)
}
func fetchremoteimage() {
guard let url = url(string: model.url) else { return }
urlsession.shared.datatask(with: url){ (data, response, error) in
if let image = uiimage(data: data!){
self.remoteimage = image
} else {
print(error ?? "")
}
}.resume()
}
}
|
基于我们的app做出来的简单效果图
widget相关资料
widgets
creating a widget extension
keeping a widget up to date
making a configurable widget
到此这篇关于详解ios14 widget 开发相关及易报错地方处理的文章就介绍到这了,更多相关ios14 widget开发内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!
相关文章
- ASP.NET本地开发时常见的配置错误及解决方法? 2025-06-10
- ASP.NET自助建站系统的数据库备份与恢复操作指南 2025-06-10
- 个人网站服务器域名解析设置指南:从购买到绑定全流程 2025-06-10
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 个人服务器网站搭建:如何选择适合自己的建站程序或框架? 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-27 50
-
Java通过调用C/C++实现的DLL动态库——JNI的方法
2025-05-27 36 -
2025-05-27 28
-
2025-05-25 24
-
2025-05-29 46



