今天来聊一聊关于小组件Timeline相关的问题。
- 我个人的理解为,小组件只要搞清楚Timeline与页面更新的关系,就差不多可以开发一个完整模块的小组件。
注意备注
创建工程
@main
小组件的入口我们可以清晰的看到这个struct
包含两块内容
kind
小组件模块的标识符
body
小组件的主题:里面包含三个参数kind
,intent
, provider
- 根据这三个入参就可以大概猜测,小组件的渲染是通过这三个参数渲染出对应的
content
而且在Provider
这个结构体中有关于SimpleEntry
相关的闭包回调
@main
struct TestWidget: Widget {
let kind: String = "TestWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
intent: ConfigurationIntent.self,
provider: Provider())
{ entry in
TestWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
复制代码
- 然后我们做的第一件事就是这个入口进行改造一下
注意代码备注
// 对这个SimpleEntry 改造一下用于打印
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
// 给一个测试的属性
var testString: String = "test"
}
// 对展示的View 也进行改造
struct TestWidgetEntryView : View {
var entry: Provider.Entry
// 展示设定的textValue指
var body: some View {
Text("\(entry.testString)")
}
}
@main
struct TestWidget: Widget {
let kind: String = "TestWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
intent: ConfigurationIntent.self,
provider: Provider()) { (entry) -> TestWidgetEntryView in
// 添加一个打印函数主要查看 content 回调的调用情况
print("[content]--\(entry.testString)")
return TestWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
// 同时对 Provider 进行改造, 在每个SimpleEntry初始化的时候添加对应的testvalue
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
testString: "placeholder")
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
testString: "getSnapshot")
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate,
configuration: configuration,
testString: "getTimeline\(hourOffset)")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
复制代码
在修改完成后我们运行一下看看
其中有两个问题
- 明明回调了5次却只显示第一次的回调
- placeholder去哪里了
我们抱着这俩问题继续往下走
首先我们去TimelineEntry
这个协议瞅一眼
发了个好东西
public protocol TimelineEntry {
/// 就是这玩意
/// The date for WidgetKit to render a widget.
var date: Date { get }
/// The relevance of a widget’s content to the user.
var relevance: TimelineEntryRelevance? { get }
}
复制代码
date
的备注可以很清晰的看到The date for WidgetKit to render a widget.
这句话我理解的意思是: 根据设定的时间来更新widget
那么接下来去验证一下我们的猜想
将更新的时间间隔单位改为秒
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
// 将小时改为秒
let entryDate = Calendar.current.date(byAdding: .second,
value: hourOffset,
to: currentDate)!
let entry = SimpleEntry(date: entryDate,
configuration: configuration,
testString: "getTimeline\(hourOffset)")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
复制代码
运行一下瞅瞅
果然在这个这个犹如读秒一样刷新了5
次
紧接着会想到之前的第二个问题:
placeholder
去哪了?
进入到官方的备注看一下写到 Provides a timeline entry representing the current time and state of a widget.
其中的current time
会不会就是当前的时间。
我们对齐在进行一次改造:我们设定为第一次更新在当前时间的三秒后
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
// 第一次更新是当前时间的三秒后
let entryDate = Calendar.current.date(byAdding: .second,
value: hourOffset + 3,
to: currentDate)!
let entry = SimpleEntry(date: entryDate,
configuration: configuration,
testString: "getTimeline\(hourOffset)")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
复制代码
但是好像并没有发生什么,那会不会是在complete回调之前才会执行这个placeholder
操作呢
延迟三秒complete
闭包
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .second,
value: hourOffset,
to: currentDate)!
let entry = SimpleEntry(date: entryDate,
configuration: configuration,
testString: "getTimeline\(hourOffset)")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
// 延迟三面后执行complete闭包
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
completion(timeline)
}
}
复制代码
这下可以看到placeholder
可以正常执行了
那么我们可以得到相应的关系
同时给大家留个作业(之后会更新作业内容)
- 如何自定义秒级刷新的小组件,并且一直读秒刷新
- 主APP如果控制小组件的显示内容
- 如何与主APP互相通讯
- IntentConfiguration 相关操作
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END