Widget 相似一个迷你版的 App,能够快速访问 App 提供的信息—比方天气、事件、笔记等。Widget 还能够充当“快捷方式”,点击它会立刻跳转到 App 的指定地位。

介绍

  • WidgetKit 通过在 iOS 主屏幕或 macOS 告诉核心搁置小部件,让用户能够随时拜访 App 中的内容。Widget 能够放弃更新,从而让用户取得最新信息。当须要查看 App 的更多细节时,Widget 会间接跳转到 App 中的适当地位。
  • Widget 有三种不同的尺寸(小号、中号和大号),能够对 Widget 进行个性化定制。
  • 要实现一个 Widget,须要给利用增加一个 Widget 扩大并只能应用 SwiftUI 来实现 Widget 的内容。

App实现

Widget 寄宿于 App,所以首先必须将 App 性能实现。

增加Widget

  1. 点击我的项目,抉择File > New > Target
  2. Application Extension中,抉择Widget Extension,而后点击Next
  3. 输出扩展名的名称。
  4. 单击Finish
  5. 此时会生成一个新文件夹,蕴含以下内容

    • 扩展名.swift
    • 扩展名.intentdefinition
    • Assets.xcassets
    • Info.plist

数据共享

App 与 Widget 能够通过网络数据和本地数据两种形式进行数据的共享。

  • 网络数据可通过 URLSession 实现数据的申请与解析。
  • 本地数据共享能够通过 App Groups,它是 iOS 8 之后推出的在 App 之间共享数据的形式,只须要简略的配置就能够实现数据的共享。(本文以此为例)

配置

  1. App 在Signing&Capabilities中关上App Groups,内容个别为group.Bundle Identifier
  2. Widget 必须在Signing&Capabilities中关上App Groups,内容与 App 保持一致。

如果文件须要共享,能够选中 App 中须要共享给 Widget 的文件,而后勾选 Widget 的 Target。

实现

配置实现当前,能够通过UserDefaultsFileManager来实现 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

  1. 原理:开发者通过 SwiftUI 构建 Views,定义Timelines为 Views 提供对应工夫所需的数据,当数据变动时,通过reload更新数据。TimelineProvider提供一组TimelineEntryReloadPolicy,用来后续刷新页面。
  2. 实现 Widget 的代码绝对比拟模版,能够从 Widget 的入口开始,缺什么补什么。563513413,不论你是大牛还是小白都欢送入驻

入口

`@mainstruct 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) {    }}`复制代码
  1. getTimeline 是最重要的办法,前面的数据刷新都会在其中实现,所以可能会在其中实现最新的网络数据和本地数据的获取,而后转成 Model 以供应用。
  2. getTimeline 的办法里有一个 policy 参数,示意刷新的机会,能够抉择.never(不刷新),.atEnd(Entry 显示结束之后主动刷新) 或 .after(date)(达到某个特定工夫后主动刷新)
  3. 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:            // 大尺寸        }    }}`复制代码
  1. Widget 能且只能应用 SwiftUI 构建界面。
  2. 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解决
`@mainstruct SwiftUIApp: App {    var body: some Scene {        WindowGroup {            ContentView()                .onOpenURL(perform: { url in                    print(url)                })        }    }}`复制代码

源代码