詳解iOS14 Widget 開發(fā)相關(guān)及易報錯地方處理
首先了解下如何創(chuàng)建
Xcode -> File -> New -> Target 找到 Widget Extension
如果你的 Widget 支持用戶配置屬性,則需要勾選這個(例如天氣組件,用戶可以選擇城市),不支持的話則不用勾選
了解下創(chuàng)建Widget后,系統(tǒng)給我們生成的文件內(nèi)容
下面這個代碼是沒有勾選 Include Configuration Intent 的地方
Provider
// Provider,顧名思義為小組件提供信息得一個struct struct Provider: TimelineProvider { public typealias Entry = SimpleEntry // 編輯屏幕時,左上角選擇添加小組件時候,第一次展示小組件會走這個方法 public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) { } // 這個方法內(nèi)可以進行網(wǎng)絡(luò)請求,拿到的數(shù)據(jù)保存在對應的 entry 中,調(diào)用 completion 之后會到刷新小組件 public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) { // 例如這是一個網(wǎng)絡(luò)請求 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.
// 我的理解是就是存儲小組件的數(shù)據(jù)的一個東西 struct SimpleEntry: TimelineEntry { let date: Date let data: Data }
PlacehodlerView
// 這個是一個默認視圖,例如網(wǎng)絡(luò)請求失敗、發(fā)生未知錯誤、第一次展示小組件都會展示這個view struct PlaceholderView : View { }
WidgetEntryView
// 這個是我們需要布局小組件長什么樣子的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 之后可能出錯的地方
如果你的app中設(shè)置了 Class Prefix 這下面這個 ConfigurationIntent.self 則需要加上對應的前綴
例如前綴是 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點擊事件
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 處理,我們看到里面有四個相同的view,即左邊圖片,右邊文字的,這個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 后,點擊每個 RecipeView 都會觸發(fā)事件,這時候你需要在主項目中的 AppDelegate 中的如下方法進行處理
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { }
關(guān)于Widget中加載網(wǎng)絡(luò)圖片的時機
當我們在func timeline(withcompletion)這個方法中請求到數(shù)據(jù)拿到圖片鏈接后,必須同步把圖片解析出來,否則直接讓對應的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) // 這里給個默認圖片 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去加載)
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)及易報錯地方處理的文章就介紹到這了,更多相關(guān)iOS14 Widget開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IOS collectionViewCell防止復用的兩種方法
這篇文章主要介紹了IOS collectionViewCell防止復用的兩種方法的相關(guān)資料,需要的朋友可以參考下2016-11-11