共计 3316 个字符,预计需要花费 9 分钟才能阅读完成。
Widget 相似一个迷你版的 App,能够快速访问 App 提供的信息—比方天气、事件、笔记等。Widget 还能够充当“快捷方式”,点击它会立刻跳转到 App 的指定地位。
介绍
- WidgetKit 通过在 iOS 主屏幕或 macOS 告诉核心搁置小部件,让用户能够随时拜访 App 中的内容。Widget 能够放弃更新,从而让用户取得最新信息。当须要查看 App 的更多细节时,Widget 会间接跳转到 App 中的适当地位。
- Widget 有三种不同的尺寸(小号、中号和大号),能够对 Widget 进行个性化定制。
- 要实现一个 Widget,须要给利用增加一个 Widget 扩大并只能应用 SwiftUI 来实现 Widget 的内容。
App 实现
Widget 寄宿于 App,所以首先必须将 App 性能实现。
增加 Widget
- 点击我的项目,抉择
File > New > Target
。 - 从
Application Extension
中,抉择Widget Extension
,而后点击Next
。 - 输出扩展名的名称。
- 单击
Finish
。 -
此时会生成一个新文件夹,蕴含以下内容
扩展名.swift
扩展名.intentdefinition
Assets.xcassets
Info.plist
数据共享
App 与 Widget 能够通过 网络数据和本地数据 两种形式进行数据的共享。
- 网络数据可通过 URLSession 实现数据的申请与解析。
- 本地数据共享能够通过 App Groups,它是 iOS 8 之后推出的在 App 之间共享数据的形式,只须要简略的配置就能够实现数据的共享。(本文以此为例)
配置
- App 在
Signing&Capabilities
中关上App Groups
,内容个别为group.Bundle Identifier
。 - Widget 必须在
Signing&Capabilities
中关上App Groups
,内容与 App 保持一致。
如果文件须要共享,能够选中 App 中须要共享给 Widget 的文件,而后勾选 Widget 的 Target。
实现
配置实现当前,能够通过 UserDefaults
或FileManager
来实现 App 与 Widget 的数据共享,这里以 UserDefaults
为例,因为 SwiftUI 提供了 @AppStorage
来简化操作。
- App
`// 蕴含 App Groups 的 UserDefaults
@AppStorage("contact", store: UserDefaults(suiteName: "group.cn.abc.yf.SwiftUI-Widget"))
// 而后在前面保留数据 ` 复制代码
- Widget
`@AppStorage("contact", store: UserDefaults(suiteName: "group.cn.abc.yf.SwiftUI-Widget"))
// 而后在前面取出数据 ` 复制代码
编写 Widget
- 原理:开发者通过 SwiftUI 构建 Views,定义
Timelines
为 Views 提供对应工夫所需的数据,当数据变动时,通过reload
更新数据。TimelineProvider
提供一组TimelineEntry
和ReloadPolicy
,用来后续刷新页面。 - 实现 Widget 的代码绝对比拟模版,能够从 Widget 的入口开始,缺什么补什么。563513413,不论你是大牛还是小白都欢送入驻
入口
`@main
struct UserWidget: Widget {
private let kind: String = "UserWidget"
public var body: some WidgetConfiguration {}}` 复制代码
kind
:字符串,惟一标识 Widget。-
WidgetConfiguration
:有两类配置,别离为StaticConfiguration
: 能够在不须要用户任何输出的状况下自行解析,能够在 Widget 的 App 中获取相干数据并发送给 Widget。IntentConfiguration
:依赖于 App 的 Siri Intent,会主动接管这些 Intent 并用于更新 Widget,用于构建动静 Widget。
.supportedFamilies
:反对不同尺寸。
内容
不论是哪种配置,都须要提供以下内容。
Entry
渲染 Widget 所需的 数据模型 ,须要恪守TimelineEntry
协定。
`struct Model: TimelineEntry {
let date: Date
// 显示的内容 Model
}` 复制代码
Provider
恪守 TimelineProvider
协定,通知 WidgetKit 何时渲染与刷新 Widget。须要实现以下 3 个办法:
`struct Provider: TimelineProvider {
// 占位视图,是一个规范的 SwiftUI View,当第一次展现或者产生谬误时都会展现该 View。func placeholder(in context: Context) -> TimelineEntry { }
// 编辑屏幕在左上角抉择增加 Widget、第一次展现时会调用该办法
func getSnapshot(in context: Context, completion: @escaping (TimelineEntry) -> Void) { }
// 进行数据的预处理,转化成 Entry
// 最初肯定要调用 completion,进而刷新 Widget
func getTimeline(in context: Context, completion: @escaping (Timeline<TimelineEntry>) -> Void) {}}` 复制代码
- getTimeline 是最重要的办法,前面的数据刷新都会在其中实现,所以可能会在其中实现最新的网络数据和本地数据的获取,而后转成 Model 以供应用。
- getTimeline 的办法里有一个 policy 参数,示意刷新的机会,能够抉择
.never(不刷新),.atEnd(Entry 显示结束之后主动刷新)或 .after(date)(达到某个特定工夫后主动刷新)
。- Widget 刷新的工夫由零碎对立决定(有时候设置了也不会本人刷新),如果须要强制刷新 Widget,能够在 App 中应用 WidgetCenter 来从新加载所有工夫线:
WidgetCenter.shared.reloadAllTimelines()
。
EntryView
屏幕上 Widget 显示的内容,能够针对不同尺寸的 Widget 设置不同的 View。
`struct EntryView: View {
var entry: Provider.Entry // 数据模型
@Environment(.widgetFamily) var family // 尺寸环境变量
@ViewBuilder
var body: some View {
switch family {
case .systemSmall:
// 小尺寸
case .systemMedium:
// 中尺寸
default:
// 大尺寸
}
}
}` 复制代码
- Widget 能且只能应用 SwiftUI 构建界面。
- Widget 实质:一个随着工夫线而更新的 SwiftUI View。
运行
- 先运行 App
- 再运行 Widget
交互
只能点击,点击会关上 App。也能够通过 .widgetURL(myDeeplink)
办法配置当 Widget 被点击时触发哪个 Deep Linking,也能够通过应用链接使 Widget 的不同局部触发不同的 Deep Linking(能够间接了解为 Widget 只是一个按钮,点按这个按钮会跳转到指定 URL 对应的页面)。
- Widget 点击
`// 某个 Widget 内容
var body: some View {
VStack {Link(destination: homeDeepLink) {Text("主页")
}
Link(destination: settingsDeepLink) {Text("设置")
}
}.widgetURL(myDeeplink)
}` 复制代码
- App 解决
`@main
struct SwiftUIApp: App {
var body: some Scene {
WindowGroup {ContentView()
.onOpenURL(perform: { url in
print(url)
})
}
}
}` 复制代码
源代码