首先了解下如何創建
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相關資料
到此這篇關于詳解ios14 widget 開發相關及易報錯地方處理的文章就介紹到這了,更多相關ios14 widget開發內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.jianshu.com/p/b0d6a14442fe