9

iOS 14 Widget 上手体验 - SwiftCafe 享受代码的乐趣

 4 years ago
source link: http://www.swiftcafe.io/post/ios14-widget
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

iOS 14 Widget 上手体验

iOS 14 Widget 上手体验

swift 发布于 2020年12月16日

今年的线上 WWDC 已经开始有一段时间了. 苹果在 iOS 14 中进行了一系列更新. 其中桌面 Widget 是最引人注意的一次革新. 在之前的很多年, 桌面 Widget 一直都存在于 Android 平台, 很多 Android 平台上利用 Widget 提供的体验, 如今在 iOS 14 中也成为可能了.

这篇文章给大家介绍如何创建一个最简单的桌面 Widget, 让大家能对 Widget 有一个快速的了解. 更深入的功能以后再和大家探讨.

创建 Widget 组件

要开发新的 Widget 组件, 首先你要准备 Xcode 12 Beta 版本, 以及一台安装了 iOS 14 Beta 版本的设备, 或者直接使用模拟器.

Widget 的基本创建并不复杂, 如果你没有时间亲自动手写代码, 看一篇这篇文章也足够了解了.

  • 在 Xcode 12 中创建一个新项目, 并且在项目设置页面里,点击左下角的箭头创建新的 Target:
1f9gq3e6kciv0jow.jpeg
  • 在弹出的组件选择窗口中, 在右上角的搜索栏中输入 widget, 然后在过滤后列表中选中 Widget Extension, 点击 Next 继续.
1f9gq3e6kcivb9xg.jpeg
  • 接着, 输入 widget 的名称, 并且取消 Include Configuration Intent 的选择, 点击 Finish.
1f9gq3e6kciw21cz.jpeg
  • Widget 创建好之后, 会看到这几个文件:

1f9gq3e6kciw8aox.jpeg

  • 创建好后, Cmd + R 运行当前程序. 回到桌面后长按空白处, 弹出设置界面, 点击左上角的添加按钮:
1f9gq3e6kciyrhce.jpeg
  • 在列表中选中我们的 app:
1f9gq3e6kciz2s6z.jpeg
  • 这样, 我们可以看到 Wdiget 的三种预览:
1f9gq3e6kciz4zfl.jpeg
  • 选择一个预览, 就可以把 Widget 添加到桌面上:
1f9gq3e6kcj0d35v.jpeg
  • 上图是我们创建的这个 Wdiget 的默认界面, 显示了一个当前时间的时钟.
了解 Widget 流程

上面我们介绍了 Widget 创建的基本流程. 接下来我们看一下如何自定义 Widget 界面.

首先, 根据目前的信息, Widget 只支持 SwiftUI 界面.

这里是 Widget 的主入口:

@main
struct widget: Widget {

private let kind: String = "widget"

public var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
widgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}

我们把上面这段代码分成3个部分来解读:

private let kind: String = "widget"

这个定义的 kind 表示 Widget 的唯一标识.

StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
widgetEntryView(entry: entry)
}

StaticConfiguration 接受3个参数.

kind 是我们刚说的, 唯一标识.
provider 可以理解为 Widget 的数据提供者 placeholder 是在 Widget 数据实际加载完成之前, 显示的一个占位符. widgetEntryView(entry: entry) 是 Widget 实际的 VIew 层. 也就是UI界面.

.configurationDisplayName("My Widget")
.description("This is an example widget.")

这两行用来设置 WIdget 添加界面中, 显示的标题和描述, 如下图:

1f9gq3e6kcjx6g4m.jpeg

使用 StaticConfiguration 的 Widget 基本结构就是这样, kind 提供唯一标识, provider 提供数据支持, placeholder 提供占位符视图. 内部返回的 widgetEntryView 作为 UI 界面.

我们逐一分析下每一个组件.

  • 先从 widgetEntryView 开始:
struct widgetEntryView : View {
var entry: Provider.Entry

var body: some View {
Text(entry.date, style: .time)
}
}

这是一个典型的 SwiftUI 视图定义, body 属性里面包含了一个 Text 视图. 将 entry 中包含的日期作为文本显示给用户. 也就是我们看到的时钟 Widget 了.

  • 然后我们再看下 placeholder:
struct PlaceholderView : View {
var body: some View {
Text("Placeholder View")
}
}

这也是一个 SwiftUI 视图, 直接在 Text 视图上显示 "Placeholder View" 这个文本. 在我们的 Wdiget 刚刚添加到界面时候, 你有机会看到这个视图, 随后真实的数据加载完成, 它就会消失, 被真正的 Widget 视图所取代.

因为这个示例 Widget 使用的是当前时间, 这个数据的加载非常快, 很可能你不一定看得到这个 PlaceholderView. 如果你的 Widget 数据加载逻辑比较耗时间, 这个 PlaceholderView 的作用就体现出来了.

  • 再来到 Wdiget 数据的核心 provider:
struct Provider: TimelineProvider {
public typealias Entry = SimpleEntry

public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
completion(entry)
}

public func timeline(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)
entries.append(entry)
}

let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}

这里使用的是 TimelineProvider 我们需要实现它的两个方法: snapshottimeline.

snapshot 可以理解为快照, 它会在 Widget 初始化的时候调用. 用于提供即时预览的数据. 比如 Widget 真实数据需要较长时间加载, 像是需要网络读取等. 那么系统会先调用 snapshot 来提供一个马上能用的数据, 这也就是说 snapshot 方法提供的数据, 应该尽量快速. 我们来看看代码:

public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
completion(entry)
}

这里用当前时间 Date(), 初始化了一个 SimpleEntry 实例. 然后传递进 completion(entry) 的调用. 这个方法你可以理解为将 entry 实体对象作为数据传递给 Widget UI 层, 也就是我们前面看到的 widgetEntryView.

SimpleEntry 继承自 TimelineEntry 用于表示 Widget 中的数据实体对象:

struct SimpleEntry: TimelineEntry {
public let date: Date
}

snapshot 的初步介绍就是这样. 接下来我们再看 timeline.

timeline 给 Widget 提供实际的数据. 系统在调用完 snapshot 提供快照数据后, 然后就会调用 timeline 获取实际的数据.

public func timeline(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)
entries.append(entry)
}

let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}

上面是 timeline 的代码, 首先 for hourOffset in 0 ..< 5 { 进行了一个 5次循环:

let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
entries.append(entry)

循环体中, 每次给 entryDate 增加1个小时, 然后将他们保存到 entries 集合中. 最后把这些集合添加到 Timeline 里:

let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)

policy: .atEnd 是 Timeline 的加载策略. 我们一共给 entries 放入了 5 个实例, 每个实例的时间都递增 1 小时. 也就是说, 每隔 1个小时, Widget 就会刷新它的界面, 然后 5次之后 entries 中的所有实例都被显示一遍.

因为我们设置了 policy: .atEnd, 就代表 5 次刷新结束后, 重新填充 Timeline. 再次调用我们上面的代码, 插入 5 的新的实例, 如此循环, 最终的效果就是每隔 1个小时, 我们的 Widget 就更新一下 UI 显示.

总结

篇幅原因, 我希望每次文章尽量不占用大家过多时间, 这样也能保证最好的阅读效率. 这篇主要介绍 Widget 最基本,也是最核心的内容. 再回顾下 Widget 的主入口:

@main
struct widget: Widget {

private let kind: String = "widget"

public var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
widgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}

再来看看几个主要组件的关系:

1f9gq9fokckbgteq.jpeg

以上就是对 Widget 的初步概览了. 如果大家有问题或者建议, 又或者发现我写的有理解不对的地方. 欢迎大家留言. 如果觉得文章对你有用, 也欢迎转发给朋友.

如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
qrcode.jpg 关注微信公众号
发现更多精彩
swift-cafe


Recommend

  • 11

    SwiftCafe 享受代码的乐趣 桌面 Widget 设计规范swift 发布于 2020年12月16日 这次继续和大家探讨 Widget 相关话题。 苹果在大多数发布的技术组件, 都会给出一套建议的设计规范。 这次 iOS 14 发布的桌面 Wid...

  • 17

    iOS 14 制作自己的桌面 Widget iOS 14 制作自己的桌面 Widgetswift 发布于 2020年12月16日 在上一篇文章中, 我和大家介绍了 iOS 14 Widget 的基本内容, 了解了组成 Widget 的主要几个组件. 这篇文章开始, 我会...

  • 43

    SwiftCafe 享受代码的乐趣 SwiftUI 初体验swift 发布于 2020年12月18日 刚刚过去的 WWDC 2019 开场演讲,可以说是干货多多, 发布了很多有意思的东西。对我们公众号的用户群来说, 我最先想要分享的那肯定就是...

  • 24

    SwiftCafe 享受代码的乐趣 Swift 5 - 对 raw string 的支持swift 发布于 2020年12月18日 Swift 5 在今年年初正式发布了, 这个年轻的语言现在也变得越来越完善了, 最近打算和大家聊聊它最新的改变. 什么是...

  • 8

    如何用好 Android 包管理系统 如何用好 Android 包管理系统swift 发布于 2020年12月18日 这是第一次在这里和大家分享 Android 相关的内容, 其实我一直是 iOS 和 Android 都在做, 只是之前这里一...

  • 11

    SwiftCafe 享受代码的乐趣 多窗口命令行的玩法swift 发布于 2020年12月18日 我们平时在开发产品的时候都免不了要使用命令行. 比如安装 CocoaPods 组件, 或者操作 Git 版本库等等. 往往会用命令行同时操作几个...

  • 6

    Magnet: 推荐一款Mac上的好工具 Magnet: 推荐一款Mac上的好工具swift 发布于 2020年12月18日 作为一个应用开发者, 不知不觉也成了各大应用商店的常客, 经常会去看排行榜, 看看有没有好的应用值得研究入手.

  • 8

    了解 Xcode 项目文件 .xcodeproj 了解 Xcode 项目文件 .xcodeprojswift 发布于 2020年12月18日 作为一名开发者, 肯定对 Xcode 的项目文件 .xcodeproj 不陌生了. 我们用 Xcode 创建的任何一个项目...

  • 15

    Swift 4.0 中对 Dictionary 的改进 Swift 4.0 中对 Dictionary 的改进swift 发布于 2020年12月18日 Swift 4 发布已经有一段时间了, 不知道大家有没有切换到 4.0 版本。 这次 4.0 更新给我最大的感受就是没有...

  • 11

    伴随春天而来的 Swift 3.1 伴随春天而来的 Swift 3.1swift 发布于 2020年12月18日 Sequence 协议添加两个方法 protocol Sequence { // ... /// Returns a subsequence by skipping elements w...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK