xcode - OS X Today Widget - 如果内容已更改,如何在 NCWidgetProviding.widgetPerformUpdateWithCompletionHandler 中检测?

标签 xcode macos cocoa swift today-extension

widgetPerformUpdateWithCompletionHandler()让我们有可能让通知中心知道 Today Extension 的内容是否发生了变化。例如:

func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
    // Refresh the widget's contents in preparation for a snapshot.
    // Call the completion handler block after the widget's contents have been
    // refreshed. Pass NCUpdateResultNoData to indicate that nothing has changed
    // or NCUpdateResultNewData to indicate that there is new data since the
    // last invocation of this method.

    if(has_new_data()) { // There was an update
        completionHandler(.NewData)
    }
    else { // nothing changed!
        completionHandler(.NoData)
    }
}

但是我们怎么知道内容是否发生了变化?在每个快照上,小部件都是从头开始实例化的。这是一个带有新 PID 的全新过程。所以你不能在你的实例中存储任何属性。如何将当前小部件内容与先前快照的内容进行比较?

我用了Core Data来存储当前的内容,方便以后对比。这很明显并且有效。但随之而来的另一个问题是:如果没有以前的快照怎么办?假设用户删除了小部件只是为了重新添加它。或者用户重新启动。没有以前的快照可能还有更多我现在想不到的原因。无论哪种方式 - 仍然有内容存储在核心数据中。如果这个旧内容和当前内容之间的比较检测到没有变化并且我返回 .NoData 小部件将最终为空,因为通知中心不会重绘内容。

您可能想知道为什么以正确的状态调用 completionHandler 而不是简单地总是返回 .NewData 对我来说如此重要。那是因为当没有变化时我遇到了一点闪烁,仍然返回 .NewData。我的小部件中有一些图像,当重新绘制小部件时,整个内容会在一毫秒内变得不可见 - 足够长以引起注意。

有什么我想念的吗?我觉得奇怪的是,Apple 提供了一种方法让我们可以选择以不同的状态进行响应,但却无法检测到我们应该响应哪种状态。

最佳答案

理论上您可以使用它来检查自上次调用以来是否有任何新内容,并传回适当的值。我想理论上它可能是多次调用时 View Controller 的同一个实例,但现在显然不是这样。检查内容是否已更改取决于应用程序和扩展程序的性质。由于您正在使用共享核心数据存储来共享数据,因此您可能会执行以下操作:

  1. 每当应用更改数据时,将当前日期保存在共享用户默认设置中。
  2. 只要今天扩展程序读取数据,就将当前日期保存在共享用户默认值中。
  3. widgetPerformUpdateWithCompletionHandler 被调用时,查找这两个日期。如果数据更改比上次扩展读取该数据的时间更近,则返回 NCUpdateResultNewData。如果不是,则返回 NCUpdateResultNoData

您还可以将这些日期保存在持久存储的元数据中,而不是共享的用户默认值中。 如果每次都是相同的 View Controller ,您可以将步骤 2 中的值保留在内存中,而不是将其保存到文件中。但这同样不是现在的工作方式,并且不清楚何时或是否会改变。

以其他方式保存数据的应用可能需要使用不同的检查,检查的细节取决于它们的应用和扩展程序的工作方式。

在实践中,对于 iOS 8.2,这真的无关紧要,因为扩展环境似乎并不关心您发回什么值。我尝试返回 NCUpdateResultNewData 并将其与每次返回 NCUpdateResultNoData 进行比较。对今天的扩展 View Controller 或其 View 的生命周期绝对没有影响。

顺便说一句,这并不总是一个不同的过程。我尝试将这一行放在今天扩展的 viewDidLoad 中:

NSLog(@"viewDidLoad pid=%d self=%@", getpid(), self);

然后我运行今天的扩展程序,在通知中心上下滚动几次,关闭通知中心,重新打开,得到以下信息:

2015-03-17 15:19:01.203 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d508cc0>
2015-03-17 15:19:14.441 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50ade0>
2015-03-17 15:19:23.784 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d619c40>
2015-03-17 15:19:29.015 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50abe0>

虽然每次都是不同的 View Controller 实例,但在每种情况下都是相同的 pid。

关于xcode - OS X Today Widget - 如果内容已更改,如何在 NCWidgetProviding.widgetPerformUpdateWithCompletionHandler 中检测?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28162895/

相关文章:

macos - AppleScript 中的 "end if"/"else if"不起作用?

cocoa - 如何从 FSSpec 获取 NSString(Unix 风格路径)

ios - Xcode 4.5 安装在我的 Mac 上的什么位置?

ios - Swift 3 转换 NSClassFromString 需要一个 ","分隔符

ios - 反转 Segue 时如何在 iOS 中传回信息?

macos - Mavericks 和 NSStatusItem 的具有多个监视器的自定义 View

macos - 如何在 Xcode 中以 Web View 启动页面?

iphone - 扩展多行 UILabel

macos - 通过 NSService 接收 URL,拖放到应用程序图标或 NSView : how to determine if it's already in sandbox?

macos - 在 Mac 上编译单个 F# 程序