詳解iOS14 Widget 開發(fā)相關(guān)及易報(bào)錯(cuò)地方處理
首先了解下如何創(chuàng)建
Xcode -> File -> New -> Target 找到 Widget Extension

如果你的 Widget 支持用戶配置屬性,則需要勾選這個(gè)(例如天氣組件,用戶可以選擇城市),不支持的話則不用勾選
了解下創(chuàng)建Widget后,系統(tǒng)給我們生成的文件內(nèi)容
下面這個(gè)代碼是沒有勾選 Include Configuration Intent 的地方
Provider
// Provider,顧名思義為小組件提供信息得一個(gè)struct
struct Provider: TimelineProvider {
public typealias Entry = SimpleEntry
// 編輯屏幕時(shí),左上角選擇添加小組件時(shí)候,第一次展示小組件會(huì)走這個(gè)方法
public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
}
// 這個(gè)方法內(nèi)可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求,拿到的數(shù)據(jù)保存在對(duì)應(yīng)的 entry 中,調(diào)用 completion 之后會(huì)到刷新小組件
public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
// 例如這是一個(gè)網(wǎng)絡(luò)請(qǐng)求
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.
// 我的理解是就是存儲(chǔ)小組件的數(shù)據(jù)的一個(gè)東西
struct SimpleEntry: TimelineEntry {
let date: Date
let data: Data
}
PlacehodlerView
// 這個(gè)是一個(gè)默認(rèn)視圖,例如網(wǎng)絡(luò)請(qǐng)求失敗、發(fā)生未知錯(cuò)誤、第一次展示小組件都會(huì)展示這個(gè)view
struct PlaceholderView : View {
}
WidgetEntryView
// 這個(gè)是我們需要布局小組件長什么樣子的view
struct StaticWidgetEntryView : View {
}
主入口
@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樣式
@main
struct MainWidgets: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
Widget1()
Widget2()
}
}
勾選 Include Configuration Intent 之后可能出錯(cuò)的地方
如果你的app中設(shè)置了 Class Prefix 這下面這個(gè) ConfigurationIntent.self 則需要加上對(duì)應(yīng)的前綴
例如前綴是 XY 則需要修改為 XYConfigurationIntent.self
@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點(diǎn)擊事件
Widget 支持三種顯示方式,分別是 systemSmall 、 systemMedium 、 systemLarge
small 樣式只能用 widgetUrl 處理
@ViewBuilder
var body: some View {
ZStack {
AvatarView(entry.character)
.widgetURL(url)
.foregroundColor(.white)
}
.background(Color.gameBackground)
}
medium 和 large 可以用 Link 或者 widgetUrl 處理,我們看到里面有四個(gè)相同的view,即左邊圖片,右邊文字的,這個(gè)view代碼如下(Link方式)
struct RecipeView: View {
let recipe: RecipeModel
var body: some View {
Link(destination: URL(string: "你的網(wǎng)址")!) {
HStack {
WebImageView(imageUrl: recipe.squareImageUrl)
.frame(width: 65, height: 65)
Text(recipe.adjName + recipe.name)
.font(.footnote)
.bold()
.foregroundColor(.black)
.lineLimit(3)
}
}
}
}
添加 Link 或 widgetUrl 后,點(diǎn)擊每個(gè) RecipeView 都會(huì)觸發(fā)事件,這時(shí)候你需要在主項(xiàng)目中的 AppDelegate 中的如下方法進(jìn)行處理
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}
關(guān)于Widget中加載網(wǎng)絡(luò)圖片的時(shí)機(jī)
當(dāng)我們?cè)趂unc timeline(withcompletion)這個(gè)方法中請(qǐng)求到數(shù)據(jù)拿到圖片鏈接后,必須同步把圖片解析出來,否則直接讓對(duì)應(yīng)的WidgetView去load url 是加載不出來的
正確的寫法
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) // 這里給個(gè)默認(rèn)圖片
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()
}
}
錯(cuò)誤的寫法(直接丟url給view去加載)
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相關(guān)資料
到此這篇關(guān)于詳解iOS14 Widget 開發(fā)相關(guān)及易報(bào)錯(cuò)地方處理的文章就介紹到這了,更多相關(guān)iOS14 Widget開發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)
這篇文章主要介紹了IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)的相關(guān)資料,需要的朋友可以參考下2017-05-05
阿里數(shù)據(jù)iOS端啟動(dòng)速度優(yōu)化心得
本篇文章給大家詳細(xì)分析了阿里數(shù)據(jù)iOS端啟動(dòng)速度優(yōu)化的知識(shí)點(diǎn)以及心得,對(duì)此有興趣的朋友參考學(xué)習(xí)下吧。2018-02-02
iOS點(diǎn)擊推送消息跳到應(yīng)用指定頁面方法
現(xiàn)在的推送用的越來越頻繁,幾乎每個(gè)應(yīng)用都開始用到了。這篇文章主要介紹了iOS點(diǎn)擊推送消息跳到應(yīng)用指定頁面方法,有需要的可以了解一下。2016-11-11

