Apple Watch显示睡眠暂停NSTimer,但WKInterfaceTimer一直在倒计时

时间:2015-04-26 07:46:45

标签: nstimer watchkit apple-watch

根据Apple文档,可以使用WKInterfaceTimer(Watch本地,倒计时但结束时不触发任何事件)和NSTimer(在计时器结束时触发方法)。所以,我的App Interface Controller中有一个NSTimer和一个WKInterfaceTimer。在模拟器上,在WatchApp运行时的所有方案中,NSTimer&当Watch处于唤醒或睡眠模式时(按照Apple手册中的说明使用模拟器锁定/解锁),WKInterfaceTimer会继续倒计时(正如他们所应)。

然而,在真实的物理观察中,2个定时器在Watch显示器睡眠(中断)和唤醒状态下表现不同。睡眠模式PAUSES接口控制器的NSTimer,但WKInterfaceTimer保持倒计时(应该如此)。

因此,2个定时器在第一次Apple Watch睡眠时立即失去同步(NSTimer暂停,WKInterfaceTimer一直在倒计时)。寻求其他人的经验以及是否有人采用了一种很好的方法来保持NSTimer和WKInterfaceTime同步,无论Watch模式如何(睡眠或清醒)。

4 个答案:

答案 0 :(得分:3)

似乎您可以存储倒计时的结束时间(例如,在NSUserDefaults中),然后在willActivate上,重新设置您的NSTimer,以便在正确的时间完成。或者,您可以调用您的iPhone应用程序来安排本地通知,但无法保证通知会传递给您的手表,因此这可能不适合您。

答案 1 :(得分:1)

我的研究结论是,根据当前发布的WatchKit的Apple文档,您需要2个计时器:Apple Watch上的WK计时器和iPhone上的NSTimer。 2个定时器应同时启动/触发,WK定时器在两个唤醒/睡眠模式下继续倒计时,NSTimer作业将在定时器结束时发出警报/发送通知。

要使两个计时器保持同步,您需要在用户启动Apple Watch WK计时器时立即触发iPhone NSTimer。

答案 2 :(得分:0)

在willActivate / didDeactivate中启动/停止WKInterfaceTimer:

class MyAwesomeInterfaceController: WKInterfaceController {

    @IBOutlet var timer: WKInterfaceTimer!

    override func willActivate() {
        super.willActivate()
        timer.start()
    }

    override func didDeactivate() {
        super.didDeactivate()
        timer.stop()
    }

}

答案 3 :(得分:0)

当Apple Watch屏幕变为空白时,它会让应用程序进入睡眠状态并暂停您已启动的Timer(s),直到应用程序返回到前台。
这在模拟器中不会发生,但会导致真实设备出现问题 但是,WKInterfaceTimer不受影响,因为它基于未来日期并在内部处理。

因此,只需比较Timer块中的2个日期并观察这两个日期之间的差异,即可在应用程序返回前景后保持计时器同步。

在下面的示例中,如果您只想更新计时器并知道倒计时何时完成,那么以下内容就足够了:

//Globally declared
@IBOutlet var interfaceTimerCountDown: WKInterfaceTimer!

var countdownToDate: Date?

func startCountdown(from count: TimeInterval) {
    //Create a future date to which the countdown will count to
    countdownToDate = Date().addingTimeInterval(count)

    //Set and Start the WKInterfaceTimer
    interfaceTimerCountDown.setDate(countdownToDate!)
    interfaceTimerCountDown.start()

    //Start your own Timer
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
        //Get current date
        let currentDate = Date()

        //Get difference between future date and current date
        let dxTimeInSeconds = self?.countdownToDate!.timeIntervalSince(currentDate) ?? 0
        print(dxTimeInSeconds)

        //Check if countdown has completed
        if dxTimeInSeconds <= 0 {
            //...do something
            print("Countdown complete!")

            timer.invalidate()
        }
    }
}

可是...

WKInterfaceTimerTimer将在几毫秒内不同步,因此如果您想要在WKInterfaceTimer的同时强制更新用户界面计数已更新,那么上述逻辑是不够的 在我的情况下,我想更新图像;像一个环形动画&amp;解决这个问题的唯一方法是将WKInterfaceTimer转储为WKInterfaceLabel +一个WKInterfaceGroup,然后在计时器块中手动更新标签和组的背景图像。

自定义解决方案:

//Declared globally

//for image; to simulate a ring animation image
@IBOutlet var group_lblTimerCount: WKInterfaceGroup!

//Simple label inside a WKInterfaceGroup
@IBOutlet var lblTimerCount: WKInterfaceLabel! //inside group_lblTimerCount

var countdownToDate: Date?

func startCountdown(from count: Int) {
    //Create a future date to which the countdown will count to
    countdownToDate = Date().addingTimeInterval(TimeInterval(count))

    //Update Label and UI
    updateTimerLabel(to: count,
                     from: count)

    //Start your own Timer
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
        //Get current date
        let currentDate = Date()

        //Get difference between future date and current date
        let dxTimeInSeconds = self?.countdownToDate!.timeIntervalSince(currentDate) ?? 0

        //Update Label and UI
        let dxTimeInSeconds_Int = Int(round(dxTimeInSeconds))
        self?.updateTimerLabel(to: dxTimeInSeconds_Int,
                               from: count)

        //Check if countdown has completed
        if dxTimeInSeconds <= 0 {
            //...do something
            print("Countdown complete!")

            //Stop timer
            timer.invalidate()
        }
    }
}

func updateTimerLabel(to count: Int, from total: Int) {
    lblTimerCount.setText("\(count)")

    updateTimerRing(to: count,
                    from: total)
}

func updateTimerRing(to count: Int, from total: Int) {
    /*
     I have 60 images for the ring named from "ring60" to "ring0"
     Generated at: http://hmaidasani.github.io/RadialChartImageGenerator
     */

    let numberOfImages = 60 //The total number of images you have
    let imageIndex = "\(Int(count * numberOfImages/total))"
    let imageName = "ring\(imageIndex)"

    group_lblTimerCount.setBackgroundImageNamed(imageName)
}

PS:我试图找到一个优雅的解决方案来解决所有这一问题,但实际上并没有找到一个现成的例子,所以我正在分享我最终的结果。

希望它有助于某人 :)