《Widgets 边看边写》第一部分:冒险开始了
作者:DylanYang,iOS 开发者, 现就职于字节跳动音乐团队
Session:https://developer.apple.com/wwdc20/10034
概览
本篇为 Widgets 边看边写(Widgets code-along)系列第一篇。
Widgets 边看边写系列正如字面含义一般,是让开发者能够跟随着完成一个 widget 的 session 系列。本系列中的 Demo App 是一些 Emoji 角色,设定上每一个角色会有一个恢复时间。本系列会用一些 Demo 中现有的一些 view 和数据结构完成一个 widget 用于在主屏幕展示每一个角色的恢复时间。
本系列中所用的 Demo 都可以在这里下载[1]。
本篇的核心在于告诉大家什么是 widgets,以及和大家一起开发一个简单的 widget。
注:由于笔者为了稳定未升级macOS到beta版本,所以是在10.15.5上跑的Xcode 12 Beta,预览功能因此失效,所以预览相关的截图使用的为 session 中原视频的截图。
什么是 Widget?
Widget 的本质是一个随着时间线而更新的 SwiftUI View。这个系列关注的就是这 view 如何更新以及何时更新。
添加 Widget Extension
首先可以打开下载的 Demo 跑一下。
App 主体很简单,展示了几个角色,点击能进入详情页看到详细信息。
我们的目标是复用详情页左上角部分的 view 制作一个 widget。
和之前 iOS 上的其他 extension 一样,我们需要新建一个 Widget Extension。
由于直接复用App主体中的 UI,我们直接将这部分文件添到 Widget Extension Target中就可。
Widget文件初探
查看创建完 Widget Extension 后默认生成的 xxxxWidget.swift 文件,Xcode 为我们生成了 Widget 所需的模版代码。
点击右侧预览窗口中的创建预览,Xcode 还会额外为我们自动创建预览相关的代码。
struct EmojiRangerWidget_Previews: PreviewProvider {
static var previews: some View {
Text("Hello, World!")
}
}
我们将 AvatarView 作为预览的 View,并设置为小号的 widget。Widget 最多支持大、中、小三种样式。
struct EmojiRangerWidget_Previews: PreviewProvider {
static var previews: some View {
AvatarView(.panda).previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
注:AvatarView 的构建需要一个 character 的入参,由于是预览界面,所以我们直接使用 .panda就可以。
实现后就能在右侧看到对应的预览界面。
@main 标识的部分是 widget 的入口。
@main
struct EmojiRangerWidget: Widget {
private let kind: String = "EmojiRangerWidget"
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
EmojiRangerWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemSmall])
}
}
-
configurationDisplayName是在添加widget时显示的标题。 -
description是显示的描述。 -
supportedFamilies是选择可以支持的尺寸,默认是大、中、小三种全支持, 可以根据实际情况调整,在这个demo中,我们只支持小尺寸。
具体的效果可以在 Widget 库(Widget Gallery)中看到。
EntryView 是我们实际展示的 view
struct EmojiRangerWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
我们将这里替换成 AvatarView,这里因为是真实要展示的 view,所以参数是要通过外部传递进来的,Entry 类就是用来携带信息的。
struct EmojiRangerWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
AvatarView(entry.character)
}
}
我们在 SimpleEntry 结构体中加入 character 参数,同时由于第一部分中不需要用到 configuration 我们先删除。
struct SimpleEntry: TimelineEntry {
public let date: Date
let character: CharacterDetail
}
Entry 是从 Timeline Provider 提供的,Timeline Provider 是 Widget 的核心引擎。
可以看到 Provider 里默认实现了两个方法。
public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), character: .panda)
completion(entry)
}
snapshot 正如快照的含义一般,是在 Widget 库中展示的时候调用的,所以我们 character 参数传固定的 .panda 就行。
public func timeline(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let entries: [SimpleEntry] = [SimpleEntry(date: Date(), character: .panda)]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
而 timeline 方法,则是当用户添加 widget 后,实际调用的。在这个篇章中,我们不会具体讲述关于 timeline 的部分,只是完成最简单的 widget,所以我们可以直接删去这段根据时间生成 entries 的过程,直接返回一个 SimpleEntry。
struct PlaceholderView : View {
var body: some View {
AvatarView(.panda)
.isPlaceholder(true)
}
}
对于 widget 还可以设置一个 PlaceHolderView, 作为 timeline 没准备好时的占位 view。新的 SwiftUI API 直接支持了 isPlaceHolder 的方法,十分方便,可以直接为我们生成类似骨架图的 UI。注:虽然session视频中已经展示了 isPlaceholder 方法,但是在已经发布的 Xcode 12 Beta1 中实际还没支持此方法。
推荐阅读
关注我们
我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。
支持作者
这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 99 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~
WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。
参考资料
演示Demo: https://developer.apple.com/documentation/widgetkit/building_widgets_using_widgetkit_and_swiftui
本文分享自微信公众号 - 老司机技术周报(LSJCoding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。