【问题标题】:What is the flow for updating complication data for Apple Watch?Apple Watch 的复杂功能数据更新流程是怎样的?
【发布时间】:2016-01-03 13:14:46
【问题描述】:

我一直在关注互联网上的很多教程来学习如何设置复杂功能。按预期设置并发症我没有问题。

直到初始时间线条目过期。 12 小时后,我不知道如何更新它以保持并发症的存在。我将在下面分享我拥有的所有内容,希望有人可以帮助我填写。

在这里,我为要在复杂功能上显示的数据创建变量。

struct data = {
var name: String
var startString: String
var startDate: NSDate
}

以下数组是该数据的容器。

var dataArray = [data]

这允许在手表锁定时显示复杂功能。

func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
    handler(.ShowOnLockScreen)
}

这允许在复杂功能上进行时间旅行。

func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
    handler([.Forward])
}

这里,我将时间线的开始时间设置为等于现在。

func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
    handler(NSDate())
}

在这里,我将时间线的结束时间设置为等于从现在开始的 12 小时。

func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
    handler(NSDate(timeIntervalSinceNow: (60 * 60 * 12)))
}

在这里,我创建了并发症的模板。这是为了在用户浏览手表上的所有并发症时看到我的并发症时向他们显示示例数据。

func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {

    let headerTextProvider = CLKSimpleTextProvider(text: "Some Data")
    let body1TextProvider = CLKSimpleTextProvider(text: "Some Data Time")
    let template = CLKComplicationTemplateModularLargeStandardBody()
    template.headerTextProvider = headerTextProvider
    template.body1TextProvider = body1TextProvider

    handler(template)
}

这将为并发症创建第一个时间线条目。启用复杂功能后,此代码将运行并立即相应地填充复杂功能。

func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {

    createData()

    if complication.family == .ModularLarge {

        if dataArray.count != 0 {

            let firstData = dataArray[0]
            let headerTextProvider = CLKSimpleTextProvider(text: firstData.name)
            let body1TextProvider = CLKSimpleTextProvider(text: firstData.startString)
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        } else {
            let headerTextProvider = CLKSimpleTextProvider(text: "No Data")
            let body1TextProvider = CLKSimpleTextProvider(text: "Create some data")
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider

            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        }

    } else {
        handler(nil)
    }

}

这是我为我目前拥有的所有数据创建时间线条目的地方。

func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {

    createData()

    var entries = [CLKComplicationTimelineEntry]()

    for dataObject in dataArray {

        if entries.count < limit && data.startDate.timeIntervalSinceDate(date) > 0 {

            let headerTextProvider = CLKSimpleTextProvider(text: dataObject.name)
            let body1TextProvider = CLKSimpleTextProvider(text: dataObject.startString)
            let template = CLKComplicationTemplateModularLargeStandardBody()
            template.headerTextProvider = headerTextProvider
            template.body1TextProvider = body1TextProvider
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(timeInterval: (-10*60), sinceDate: data.startDate), complicationTemplate: template)
            entries.append(timelineEntry)

        }

    }

    handler(entries)

}

这告诉手表何时更新并发症数据。

func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
    handler(NSDate(timeIntervalSinceNow: 60 * 60 * 6))
}

这是我遇到问题的地方。

如何创建新数据并重新加载时间线?什么是流量?我不是要延长时间线,而是要完全取代它。我完全不知所措。 Apple's docs 在这一点上非常模糊。我知道我需要实现以下方法,但我不知道如何。有人可以帮我填写此代码吗?

func requestedUpdateDidBegin() {
    createData() //I assume createData() goes here? If so, how do I populate the new timeline entries based on the results?
}

func requestedUpdateBudgetExhausted() {
    //This can't possibly be the case as I haven't gotten it to work once.
}

func reloadTimelineForComplication(complication: CLKComplication!) {
      //This method appears to do nothing.
}

更新:

多亏了 El Tea,我才能正常工作。我需要将 CLKComplicationServer 的实例添加到 requestedUpdateDidBegin 并将 reloadTimeline 方法放入其中。

这是更新后的代码:

func requestedUpdateDidBegin() {
    print("Complication update is starting")

    createData()

    let server=CLKComplicationServer.sharedInstance()

    for comp in (server.activeComplications) {
        server.reloadTimelineForComplication(comp)
        print("Timeline has been reloaded!")
    }

}

func requestedUpdateBudgetExhausted() {
    print("Budget exhausted")
}

【问题讨论】:

  • 曾经在ComplicationController 方面遇到问题,您似乎无法通过WCSession: 获取您在ExtensionDelegate 中设置的数据?基本上在你的createData() 函数中的任何代码中,如果它是通过let myDelegate = WKExtension.sharedExtension().delegate as! ExtensionDelegateExtensionDelegate 中提取的,但实际上并没有提取任何数据。我被困在这里:stackoverflow.com/questions/35542729/…

标签: apple-watch watchos-2 apple-watch-complication clockkit


【解决方案1】:

按时间间隔进行的复杂功能刷新流程遵循以下顺序:

  • iOS 调用您的函数 requestedUpdateDidBegin()requestedUpdateBudgetExhausted()(如果您的预算用尽,在您获得更多执行时间之前,您所做的任何事情都不会导致更新。)
  • requestedUpdateDidBegin() 内部 必须调用reloadTimelineForComplication()extendTimelineForComplication() 来指定要重新加载或添加数据的并发症。如果你不这样做,什么都不会发生!
  • 根据您调用的是reload 还是extend,iOS 会调用getCurrentTimelineEntryForComplication()getTimelineEntriesForComplication() 中的一个或两个
  • 无论您是否更新了复杂功能,iOS 都会致电 getNextRequestedUpdateDateWithHandler() 以了解您下次何时需要重复上述步骤。

注意:最后两个步骤不一定要按此顺序进行。

该过程以这种方式运行,因此 iOS 不会要求您重复重新生成相同的数据。它让您有机会在requestedUpdateDidBegin() 中决定您的并发症是否需要更新。如果没有,您的代码应该只是返回。 (这会减少您的复杂功能的执行时间,并有助于避免 iOS 因使用了每日预算而切断您的应用程序的进一步更新)。但是如果你确实有新数据,你需要通过调用reloadTimelineForComplication()extendTimelineForComplication()告诉iOS

据我所知,除了您没有在requestedUpdateDidBegin() 中请求重新加载或扩展之外,您在那里编写的所有内容看起来都不错。您的复杂功能可能会在多个位置显示在表盘上,并且不同的模板可能具有不同的显示行为,因此您必须使所有这些都无效。这是我的代码的样子:

func requestedUpdateDidBegin() {

    //Not shown: decide if you actually need to update your complication.
    //If you do, execute the following code:

    let server=CLKComplicationServer.sharedInstance()

    for comp in (server.activeComplications) {
        server.reloadTimelineForComplication(comp)
    }
}

请注意,除了时间间隔之外,还有其他方法可以启动刷新,包括推送警报、在您的手表应用运行时执行重新加载,或者使用带有 WCSession 的 Watch Connectivity 框架让您的手机应用发送更新数据以通过以下方式立即显示transferCurrentComplicationUserInfo()See Updating Your Complication Data 在 Apple 的文档中了解更多信息。

我在模拟器测试更新间隔短至十分钟方面取得了成功。由于执行时间预算,您可能不应该在真实手表上频繁更新,但这将使您无需等待 12 小时即可测试您的代码。

【讨论】:

  • 我正在尝试!一定是我没有包含 CLKComplicationServer。如果它最终工作,我会告诉你。
  • 太棒了!并发症很简洁,但目前仍然非常缺乏有关它们的信息。很高兴你让它运行起来。
  • 如果你的 createData() 函数是同步的,这一切都很好,但是如果它需要从 iOS 应用程序获取数据呢?让 iOS 应用程序调用 transferCurrentComplicationUserInfo 的最佳方法是什么?一个 hacky 的替代方法是在延迟计时器上调用 requestUpdateDidBegin 调用 reloadTimelineForComplication。
  • 未经测试,但我的方法将取决于 iOS 应用程序上的数据是如何生成的。如果手机上的数据是自己生成的,按照自己的节奏,我想我会像你说的那样使用transferCurrentComplicationUserInfo(),在需要时从手机发送到手表。但是,如果手表仍处于控制状态并正在获取信息,请使用带有sendMessage:replyHandler:errorHandler: 的 WCSession 向电话询问信息。要么使用replyhandler 调用reloadTimelineForComplication,要么不使用处理程序并返回transferCurrentCOmplicationUserInfo()。祝你好运!
  • 请注意,当调用 requestedUpdateBudgetExhausted 时,您可以在该调用中再进行一次更新。它就像一个标记,再一次,然后您将不得不等待 x 金额,直到手表决定补充您的预算
【解决方案2】:

El Tea's answer 详细介绍了如何更新 watchOS 2 的复杂功能。

在 watchOS 3 中,让您的复杂功能保持最新的推荐方法是使用 background refresh app tasks。您可以使用一系列后台任务将schedulehandle 在后台唤醒您的应用扩展程序:

这更加实用且节能,因为它不使用任何复杂功能的日常执行预算来获取数据或更新模型。

它还避免了不建议的方法带来的任何复杂性,这些方法试图异步获取复杂数据源中的数据,而这些数据源只是为了立即响应请求。

我提供了更多信息,以及指向 WWDC 视频和示例代码的链接,in a different answer

总结 watchOS 3 的变化

使用计划的后台任务而不是getNextRequestedUpdateDateWithHandler()

在应用任务中重新加载或延长您的时间线,而不是在 requestedUpdateDidBegin() 中。

【讨论】:

  • 这是一篇很棒的文章,但 watchOS 3 不对外公开。
  • 请注意,目前似乎无法使用WKWatchConnectivityRefreshBackgroundTask,请参阅here。它甚至不适用于 Apple 的演示项目 QuickSwitch
  • 有没有办法在没有 iPhone 或服务器的情况下使用 watchOS 3 的新功能?如果我们希望 watchOS 调度后台任务怎么办?在不使用 WKWatchConnectivityRefreshBackgroundTask 或 WKURLSessionRefreshBackgroundTask 的情况下如何做到这一点?假设手表上的模型数据不断更新,与配对的 iPhone 无关。在不知道已配对手机的情况下,您将如何直接从 watchOS 方法触发并安排后台任务?
  • 非常正确。事实上,在 watchOS 4 中,使用后台刷新应用程序任务是强制性的,而不是旧的 requestedUpdateDidBegin() 方式。能否给bkwebhero的代码贴一个如何实现后台刷新应用任务的例子?
猜你喜欢
  • 2016-04-05
  • 1970-01-01
  • 2017-11-22
  • 1970-01-01
  • 1970-01-01
  • 2016-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多