Creating a Widget Extension(创建小部件扩展)
添加并配置扩展程序,以在主屏幕,“今日视图”或“通知中心”上显示应用程序的内容。
总览
- 窗口小部件显示相关的内容,使用户可以快速访问您的应用以获取更多详细信息。
- 您的应用程序可以提供多种小部件,使用户可以专注于对他们而言最重要的信息。
- 他们可以添加同一窗口小部件的多个副本,从而根据其独特的需求和布局定制每个窗口小部件。
- 如果您在窗口小部件中包含自定义意图,则用户可以分别个性化每个窗口小部件。
- 小部件支持多种尺寸;您可以选择最适合您应用内容的尺寸。
- 在可用空间有限的情况下,您的小部件应专注于呈现人们最重视的信息。
将小部件添加到您的应用程序需要进行少量的设置,并且需要一些有关用户界面的配置和样式的决定。
小部件使用SwiftUI视图显示其内容。有关更多信息,请参见SwiftUI。
将小部件目标添加到您的应用
- 窗口小部件扩展模板提供了创建窗口小部件的起点。
- 单个小部件扩展可以包含多种小部件。例如,一个体育应用程序可能有一个显示团队信息的小部件,另一个显示游戏时间表的小部件。
- 一个小部件扩展可以包含两个小部件。
-尽管建议将所有窗口小部件包含在一个窗口小部件扩展中,但如有必要,您可以添加多个扩展。
步骤如下:
- 在Xcode中打开您的应用程序项目,然后选择“文件”>“新建”>“目标”。
- 从“应用程序扩展”组中,选择“窗口小部件扩展”,然后单击“下一步”。
- 输入您的扩展名。
- 如果窗口小部件提供了用户可配置的属性,请选中“包括配置意图”复选框。
- 单击完成。
添加配置详细信息
小部件扩展模板提供符合Widget协议的初始小部件实现。此窗口小部件的body属性确定窗口小部件是否具有用户可配置的属性。有两种配置:
StaticConfiguration:对于没有用户可配置属性的窗口小部件。例如,显示一般市场信息的股市窗口小部件,或显示趋势头条的新闻窗口小部件。
IntentConfiguration:对于具有用户可配置属性的窗口小部件。您使用SiriKit自定义意图来定义属性。例如,需要一个城市的邮政编码的天气小部件,或者需要一个跟踪号的包裹跟踪小部件。
包括配置意图复选框确定Xcode使用哪种配置。当您选中此复选框时,Xcode将使用意图配置;否则,它使用静态配置。
要初始化配置,请提供以下信息:
种类:标识小部件的字符串。这是您选择的标识符,并且应描述小部件所代表的内容。
提供者:符合条件的对象会产生一个时间线,告诉WidgetKit何时渲染窗口小部件。
时间轴包含您定义的自定义类型。时间轴条目标识您希望WidgetKit更新小部件内容的日期。
包括需要以自定义类型呈现窗口小部件视图所需的属性。
TimelineProvider
TimelineEntry内容闭包:包含SwiftUI视图的闭包。
WidgetKit调用此方法以呈现窗口小部件的内容,并从提供程序传递参数。TimelineEntry自定义意图:定义用户可配置属性的自定义意图。
有关添加自定义项的更多信息,请参见
制作可配置窗口小部件。
使用修饰符提供其他配置详细信息,包括显示名称,描述和小部件支持的系列。以下代码显示了一个小部件,它为游戏提供了常规的,不可配置的状态:
@main
struct GameStatusWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "com.mygame.game-status",
provider: GameStatusProvider(),
) { entry in
GameStatusView(entry.gameStatus)
}
.configurationDisplayName("Game Status")
.description("Shows an overview of your game status")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}
小部件的提供程序为小部件生成时间线,并在每个条目中包含游戏状态详细信息。
当每个时间线条目的日期到达时,WidgetKit会调用content闭包以显示小部件的内容。
最后,修饰符指定小部件库中显示的名称和描述,并允许用户选择小,中或大版本的小部件。
为了使某个应用程序的窗口小部件出现在窗口小部件库中,用户必须在安装该应用程序后至少启动一次包含该窗口小部件的应用程序。
注意@main此小部件上属性的用法。GameStatusWidget此属性指示,它是窗口小部件扩展的入口点,表示该扩展包含单个窗口小部件。要支持多个小部件,请参阅在App Extension中声明多个小部件。
提供时间表条目
时间线提供者会生成一个由时间线条目组成的时间线,每个条目都指定更新窗口小部件内容的日期和时间。游戏状态窗口小部件可能会定义其时间轴条目,以包含代表游戏状态的字符串,如下所示:
struct GameStatusEntry: TimelineEntry {
var date: Date
var gameStatus: String
}
要在小部件库中显示小部件,WidgetKit要求提供程序提供一个预览快照。
您可以通过检查传递给getSnapshot(in:completion:)方法的上下文参数的isPreview属性来识别这个预览请求。当isPreview为true时,WidgetKit将在小部件库中显示您的小部件。作为响应,您需要快速创建预览快照。如果小部件需要花费时间从服务器生成或获取资产或信息,则使用示例数据。
在以下代码中,如果游戏状态小部件的提供者尚未完成从其服务器中获取状态的信息,则它通过显示空状态来实现快照方法:
struct GameStatusProvider: TimelineProvider {
var hasFetchedGameStatus: Bool
var gameStatusFromServer: String
func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
let date = Date()
let entry: GameStatusEntry
if context.isPreview && !hasFetchedGameStatus {
entry = GameStatusEntry(date: date, gameStatus: "—")
} else {
entry = GameStatusEntry(date: date, gameStatus: gameStatusFromServer)
}
completion(entry)
}
请求初始快照之后,WidgetKit调用getTimeline(in:completion:)从提供程序请求一个常规时间轴。
时间轴由一个或多个时间轴条目和一个通知WidgetKit何时请求后续时间轴的重载策略组成。
下面的例子展示了游戏状态小部件的提供商如何从服务器生成一个包含当前游戏状态的单个条目的时间轴,以及一个在15分钟内请求新的时间轴的重载策略:
struct GameStatusProvider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
// Create a timeline entry for "now."
let date = Date()
let entry = GameStatusEntry(
date: date,
gameStatus: gameStatusFromServer
)
// Create a date that's 15 minutes in the future.
let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: date)!
// Create the timeline with the entry and a reload policy with the date
// for the next update.
let timeline = Timeline(
entries:[entry],
policy: .after(nextUpdateDate)
)
// Call the completion to pass the timeline to WidgetKit.
completion(timeline)
}
}
在此示例中,如果窗口小部件不具有服务器的当前状态,则它可以存储对完成的引用,向服务器执行异步请求以获取游戏状态,并在该请求完成时调用完成。
有关生成时间轴(包括在小部件中处理网络请求)的更多信息,请参阅使小部件保持最新。
显示占位符小部件
当WidgetKit第一次显示小部件时,它将小部件视图呈现为占位符。
占位符视图显示小部件的通用表示,让用户大致了解小部件显示的内容。
WidgetKit调用placeholder(in:)
来请求表示小部件占位符配置的条目。
例如,游戏状态小部件将实现如下方法:
struct GameStatusProvider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
GameStatusEntry(date: Date(), gameStatus: "—")
}
}
为了将小部件的视图呈现为占位符,WidgetKit使用redacted(reason:)
视图修饰符来指定reason的placeholder(占位符)。
要防止小部件的视图层次结构中的视图自动呈现为placeholder(占位符),请使用unredacted()视图修饰符。
如果在窗口小部件扩展中启用了数据保护功能,则当数据保护权利指定以下值并且满足相关条件时,WidgetKit会将窗口小部件呈现为占位符:
- NSFileProtectionComplete或NSFileProtectionCompleteUnlessOpen,并且设备已锁定。
- NSFileProtectionCompleteUntilFirstUserAuthentication,并且用户尚未验证。
有关配置数据保护的更多信息,请参见Data Protection Entitlement。
在小部件中显示内容
小部件通常通过组合其他SwiftUI视图来使用SwiftUI视图定义其内容。如上所示,窗口小部件的配置包含WidgetKit调用以呈现窗口小部件内容的闭包。
当用户从窗口小部件库中添加窗口小部件时,他们从窗口小部件支持的族中选择特定的族(小,中或大)。小部件的内容封闭必须能够呈现小部件支持的每个系列。
WidgetKit在SwiftUI环境中设置相应的系列和其他属性,例如配色方案(浅色或深色)
在上面显示的游戏状态小部件的配置中,内容闭包使用一个GameStatusView来显示状态。因为小部件支持所有三个小部件系列,所以它使用widgetFamily来决定显示哪个特定的SwiftUI视图,如下所示:
struct GameStatusView : View {
@Environment(\.widgetFamily) var family: WidgetFamily
var gameStatus: GameStatus
@ViewBuilder
var body: some View {
switch family {
case .systemSmall: GameTurnSummary(gameStatus)
case .systemMedium: GameStatusWithLastTurnResult(gameStatus)
case .systemLarge: GameStatusWithStatistics(gameStatus)
default: GameDetailsNotAvailable()
}
}
}
`@ViewBuilder`
从闭包构造视图的自定义参数属性
自定义结构视图时
对于小号,小部件使用一个视图来显示其在游戏中的回合的简单摘要。
对于中号,它显示状态,指示最后一轮的结果。
对于大号,因为有更多可用空间,它可以显示每个玩家的跑步统计信息。
如果该家族是未知类型,它将显示默认视图,表明游戏状态不可用。
视图使用@ViewBuilder声明它的主体,因为它使用的视图类型不同。
对于可配置小部件,提供程序符合IntentTimelineProvider。
这个提供程序执行与TimelineProvider相同的功能,但是它合并了用户在小部件上定制的值。
这些定制在传递给getSnapshot(for:in:completion:)和getTimeline(for:in:completion:)的配置参数中对intent timeline提供程序可用。
您通常将用户配置的值作为自定义时间轴条目类型的属性,因此小部件的视图可以获得详细信息。
小部件显示只读信息,不支持交互元素,例如滚动元素或开关。
呈现窗口小部件的内容时,WidgetKit会忽略交互元素。
有关WidgetKit支持的视图的列表,请参见SwiftUI视图。
将动态内容添加到您的小部件
尽管小部件的显示基于视图的快照,但是您可以使用各种SwiftUI视图,这些视图在小部件可见时会继续更新。
有关提供动态内容的更多信息,请参见使窗口小部件保持最新。
回应用户互动
当用户与您的小部件交互时,系统将启动您的应用以处理请求。
当系统激活您的应用程序时,导航至与小部件内容相对应的详细信息。
您的窗口小部件可以指定一个URL,以通知应用程序显示哪些内容。
要在窗口小部件中配置自定义URL:
对于所有窗口小部件,将视图修饰符widgetURL(_:)
添加到窗口小部件的视图层次结构中的视图。
如果窗口小部件的视图层次结构包含多个修饰符widgetURL,则该行为未定义。对于使用systemMedium或systemLarge的小部件,向小部件的视图层次结构中添加一个或多个链接控件。你可以同时使用widgetURL和Link控件。
如果交互的目标是链接控件,系统将使用该控件中的URL。
对于小部件中其他地方的交互,系统使用widgetURL视图修饰符中指定的URL。
@ViewBuilder
var body: some View {
ZStack {
AvatarView(entry.character)
.widgetURL(entry.character.url)
.foregroundColor(.white)
}
.background(Color.gameBackground)
}
`ViewBuilder`
如果窗口小部件显示字符列表,则列表中的每个项目都可能在Link控件中。每个Link控件将为其显示的特定字符指定URL。
当小部件接收到交互时,系统激活包含应用程序,并将URL传递给onOpenURL(执行:)、application(:open:options:)或application(:open:),这取决于应用程序使用的生命周期。
如果小部件不使用widgetURL或Link控件,系统将激活包含的应用程序,并将NSUserActivity传递给onContinueUserActivity(:perform:)、application(:continue:restorationHandler:)或application(_:continue:restorationHandler:)。
用户活动的userInfo字典包含与用户交互的小部件的详细信息。
使用WidgetCenter.UserInfoKey从Swift代码中访问这些值。
要从Objective-C访问userInfo值,使用键WGWidgetUserInfoKeyKind和WGWidgetUserInfoKeyFamily代替。
对于使用IntentConfiguration的小部件,用户活动的交互属性包含小部件的INIntent。
在您的应用扩展中声明多个小部件
- 上面的GameStatusWidget示例使用@main属性为小部件扩展指定单个入口点。
- 要支持多个小部件,声明一个符合WidgetBundle的结构,该结构将多个小部件在其主体属性中组合在一起。
- 在这个小部件包结构中添加@main属性,以告诉WidgetKit您的扩展支持多个小部件。
例如,如果游戏应用程序具有第二个用于显示角色状态的小部件,而第三个用于显示排行榜的小部件,则将它们分组在一起,如下所示:
@main
struct GameWidgets: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
GameStatusWidget()
CharacterDetailWidget()
LeaderboardWidget()
}
}
`WidgetBundleBuilder`
构造小部件包主体的自定义属性
`WidgetBundle`
用于从单个小部件扩展公开多个小部件的容器
另外阅读
使用窗口小部件套件和SwiftUI构建窗口小部件
创建窗口小部件以在主屏幕上显示应用程序的内容,以及用于用户可自定义设置的自定义意图。
Fruta:使用SwiftUI构建功能丰富的应用程序
创建共享代码库以构建提供小部件和应用程序剪辑的多平台应用程序。
Widget
在主屏幕或通知中心中显示的小部件的配置和内容。
WidgetBundle
一个容器,用于从单个窗口小部件扩展中显示多个窗口小部件。
StaticConfiguration
描述没有用户可配置选项的窗口小部件内容的对象。
WidgetFamily
小部件使用的模板:小,中或大。