了解Widget
官方學(xué)習(xí)WidgetKit Demo
Widgets顯示相關(guān)的、可瀏覽的內(nèi)容,允許用戶快速訪問你的App以獲取更多詳細(xì)信息。你的App可以提供多種小組件,讓用戶專注于對(duì)他們最重要的信息。
向你的應(yīng)用添加一個(gè)小組件需要少量的設(shè)置,以及一些關(guān)于你的用戶界面的配置和風(fēng)格的決定。小組件使用SwiftUI視圖顯示其內(nèi)容。有關(guān)詳細(xì)信息,請(qǐng)參見SwiftUI。
添加一個(gè)Widget到你的App
Widget Extension模板為創(chuàng)建小組件提供了一個(gè)起點(diǎn)。一個(gè)Widget Extension可以包含多種類型的小組件。例如,一個(gè)體育應(yīng)用程序可能有一個(gè)小組件顯示團(tuán)隊(duì)信息,另一個(gè)小組件顯示比賽日程,這個(gè)例子一個(gè)Widget Extension包含了兩個(gè)組件。建議在一個(gè)Widget Extension中包含你的App所有小組件。
添加步驟如下:
- 在Xcode中打開你的項(xiàng)目,然后選擇 File > New > Target 。
-
從“Application Extension”組中,選擇“Widget Extension”,然后單擊“Next”。如下圖:
1.png -
然后,給小組件命令,如下:
2.png - 如果小組件提供用戶可配置的屬性,請(qǐng)選中include configuration intent復(fù)選框。
-
點(diǎn)擊Finish,會(huì)生成你們組件文件,如:AppWidget.swift、AppWidget.intentdefinition、Assets.xcassets、Info.plist 。如下圖:
3.png
然后你可以運(yùn)行到iOS14的設(shè)備上看效果,會(huì)看到默認(rèn)的小時(shí)鐘組件。
配置描述
widget extension模板提供了一個(gè)符合widget協(xié)議的初始widget實(shí)現(xiàn)。此小組件的body屬性決定小組件是否具有用戶可配置的屬性。有兩種配置:
-
StaticConfiguration:對(duì)于沒有用戶可配置屬性的小組件。例如,顯示一般市場(chǎng)信息的股市小組件,或顯示趨勢(shì)標(biāo)題的新聞小組件。 -
IntentConfiguration:對(duì)于具有用戶可配置屬性的小組件。你可以使用SiriKit自定義內(nèi)容來定義屬性。例如,需要一個(gè)城市的郵政編碼的天氣小組件,或者需要跟蹤號(hào)碼的包裹跟蹤小組件。
要初始化配置,請(qǐng)?zhí)峁┮韵滦畔ⅲ?/li>
- Kind: 標(biāo)識(shí)小部件的字符串。這是一個(gè)你選擇的標(biāo)識(shí)符,應(yīng)該描述小部件所代表的內(nèi)容。
- Provider:一個(gè)符合TimelineProvider的對(duì)象,它生成一個(gè)時(shí)間軸,告訴WidgetKit何時(shí)呈現(xiàn)小組件。時(shí)間線包含您定義的自定義TimelineEntry類型。時(shí)間線條目標(biāo)識(shí)您希望WidgetKit更新小組件內(nèi)容的日期。包括小組件視圖需要在自定義類型中呈現(xiàn)的屬性。
- Placeholder View: WidgetKit第一次使用SwiftUI視圖呈現(xiàn)小組件。占位符是小組件的通用表示,沒有特定的配置或數(shù)據(jù)。
- Content Closure: 包含SwiftUI視圖的閉包。WidgetKit調(diào)用它來呈現(xiàn)小組件的內(nèi)容,從提供者傳遞一個(gè)TimelineEntry參數(shù)。
- Custom Intent: 定義用戶可配置屬性的自定義意圖。有關(guān)添加自定義更多信息,請(qǐng)參閱 Making a Configurable Widget。
例如下面代碼顯示IntentConfiguration類型的組件:
@main
struct AppWidget: Widget {
private let kind: String = "AppWidget" //小組件的標(biāo)識(shí)
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
AppWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget") //小組件的名稱
.description("This is an example widget.") //小組件的描述
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) //用戶選擇小部件、中版本或大版本的組件
}
}
在上面代碼中,小組件使用PlaceholderView()作為占位符視圖,并在內(nèi)容閉包中使用AppWidgetEntryView。占位符視圖顯示小組件的通用表示,讓用戶大致了解小部件顯示的內(nèi)容。不要在占位符視圖中包含實(shí)際數(shù)據(jù)。例如,使用灰色框表示文本行,或使用灰色圓表示圖像。
Provider為小組件生成一個(gè)時(shí)間軸,當(dāng)每個(gè)時(shí)間軸條目的日期到達(dá)時(shí),WidgetKit調(diào)用content閉包來更新顯示小組件的內(nèi)容。
注意這個(gè)小組件上@main屬性的用法。此屬性指示AppWidget是小組件擴(kuò)展的入口點(diǎn),表示該擴(kuò)展包含一個(gè)小組件。
TimelineProvider
TimelineEntry為TimelineProvider提供數(shù)據(jù)信息來源,如下所示:
struct SimpleEntry: TimelineEntry {
public let date: Date
public let configuration: ConfigurationIntent
}
為了在窗口小組件庫中顯示小組件,WidgetKit要求提供者提供預(yù)覽快照。你可以通過檢查此 snapshot(for:with:completion:)方法中context的isPreview屬性來判斷小組件顯示情況。當(dāng)isPreview為true時(shí),WidgetKit會(huì)在widget庫中顯示你的widget。對(duì)于有網(wǎng)絡(luò)數(shù)據(jù)的話,你需要先提供一個(gè)默認(rèn)數(shù)據(jù)顯示快照。如下示例代碼:
struct Provider: IntentTimelineProvider {
public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
print(context.isPreview) //顯示widget的顯示情況
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
}
WidgetKit框架要求我們用timeline(for:with:completion:)來控制刷新小組件的時(shí)間。
struct Provider: IntentTimelineProvider {
public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
print(context.isPreview) //顯示widget的顯示情況
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
public func timeline(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration)
entries.append(entry)
}
// Create the timeline with the entry and a reload policy with the date
// for the next update.
let timeline = Timeline(entries: entries, policy: .atEnd)
// Call the completion to pass the timeline to WidgetKit.
completion(timeline)
}
}
小組件的顯示內(nèi)容
小組件UI使用SwiftUI來編寫的,當(dāng)用戶從widget庫中添加你的widget時(shí),他們會(huì)從widget支持的組件中選擇特定的組件(小型、中型或大型),widget的內(nèi)容閉包必須能夠呈現(xiàn)widget支持的每個(gè)組件。根據(jù)組件類型可以不同的實(shí)現(xiàn),代碼示例如下:
struct AppWidgetEntryView : View {
@Environment(\.widgetFamily) var family: WidgetFamily
var entry: Provider.Entry
//view使用@ViewBuilder聲明,因?yàn)樗褂玫膙iew類型不同。
@ViewBuilder
var body: some View {
switch family {
case .systemSmall:
Text(entry.date, style: .time)
case .systemMedium:
Text(entry.date, style: .time)
case .systemLarge:
Text(entry.date, style: .time)
default:
Text(entry.date, style: .time)
}
}
}
小組件顯示只讀信息,不支持交互元素,如滾動(dòng)元素或開關(guān)。
在一個(gè)Widget Extension創(chuàng)建多個(gè)小組件
要支持多個(gè)小組件,需要聲明一個(gè)符合WidgetBundle的結(jié)構(gòu),WidgetBundle在其body屬性中將多個(gè)小組件組合在一起。在這個(gè)Widget bundle結(jié)構(gòu)上添加@main屬性,告訴WidgetKit你的擴(kuò)展支持多個(gè)widget。代碼示例:
@main
struct AppWidgets: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
AppWidget()
//可以繼續(xù)添加其它Widget
}
}


