Apple Watch 显示器睡眠暂停 NSTimer,但 WKInterfaceTimer 一直倒计时
Posted
技术标签:
【中文标题】Apple Watch 显示器睡眠暂停 NSTimer,但 WKInterfaceTimer 一直倒计时【英文标题】:Apple Watch display sleep pauses NSTimer, but WKInterfaceTimer keeps counting down 【发布时间】:2015-07-04 16:28:00 【问题描述】:根据 Apple 文档,将同时使用 WKInterfaceTimer(Watch 本地,倒计时但在结束时不触发任何事件)和 NSTimer(在计时器结束时触发方法)。所以,我的 App Interface Controller 中有一个 NSTimer 和一个 WKInterfaceTimer。在模拟器上,在 WatchApp 运行时的所有方案中,NSTimer 和 WKInterfaceTimer 在 Watch 处于唤醒或睡眠模式时(按照 Apple 手册中的说明使用模拟器锁定/解锁)会继续倒计时(应该如此)。
但是,在真正的物理 Watch 上,这 2 个计时器在 Watch 显示睡眠(停电)和唤醒状态时表现不同。睡眠模式暂停了接口控制器的 NSTimer,但 WKInterfaceTimer 一直在倒计时(应该如此)。
因此,第一次物理 Apple Watch 睡眠时,2 个计时器立即不同步(NSTimer 暂停,WKInterfaceTimer 继续倒计时)。寻求其他人的经验,以及是否有人实施了一种使 NSTimer 和 WKInterfaceTime 保持同步的好方法,无论 Watch 模式如何(睡眠或清醒)。
【问题讨论】:
【参考方案1】:您似乎可以存储倒计时的结束时间(例如,在NSUserDefaults
中),然后在willActivate
上,重新设置您的 NSTimer 以便它在正确的时间完成。或者,您可以调用您的 iPhone 应用程序来安排本地通知,但不能保证通知会发送到您的手表,因此这可能不适合您。
【讨论】:
谢谢,迈克。我曾想过使用 didDeactivate(睡眠,存储)和 willActivate(唤醒,再次检索和重置 NSTimer),但这会发生很多次,因为睡眠/唤醒如此频繁(每次你扭动手腕时)。我尝试了 iPhone 的方法,通知实际上被传递到了 Watch(ios 确定了这一点),但是当 Watch 理论上应该在本地轻松处理时间事件(没有 iPhone)时,这听起来像是不必要的开销。仍在研究中,但再次感谢您的反馈。 祝您搜索顺利。虽然我希望你能找到另一种方法,但我认为当前版本的 WatchKit 将限制你使用这些技术。【参考方案2】:我的研究结论是,根据 Apple 当前版本的 WatchKit 文档,您将需要 2 个计时器:Apple Watch 上的 WK 计时器和 iPhone 上的 NSTimer。 2 个定时器应该同步启动/触发,WK 定时器在唤醒/睡眠模式下继续倒计时,NSTimer 的工作是在定时器结束时发出警报/发送通知。
要使两个计时器保持同步,您需要在用户启动 Apple Watch WK 计时器时立即触发 iPhone NSTimer。
【讨论】:
这对 WatchOS2 仍然有效吗?【参考方案3】:当 Apple Watch 屏幕变为空白时,它会使应用进入睡眠状态并暂停您已启动的 Timer
(s),直到应用重新回到前台。
这在模拟器中不会发生,但会在真实设备上引起问题。
但是,WKInterfaceTimer
不受影响,因为它基于未来日期并在内部处理。
因此,要在应用返回前台后保持计时器同步,只需比较 Timer
块中的两个日期并观察这两个日期之间的差异即可。
在以下示例中,如果您只想更新计时器并知道倒计时何时完成,那么以下内容就足够了:
//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()
但是……
WKInterfaceTimer
和 Timer
将在几毫秒内不同步,因此如果您想在 WKInterfaceTimer
计数更新的同时在更新 UI,那么上面的逻辑是不够的。
就我而言,我想更新图像;就像环形动画一样,我解决它的唯一方法是将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:我试图找到一个优雅的解决方案来解决所有这些问题,但找不到一个现成的例子,所以我分享了我最终得到的结果。
希望对某人有所帮助 :)
【讨论】:
以上是关于Apple Watch 显示器睡眠暂停 NSTimer,但 WKInterfaceTimer 一直倒计时的主要内容,如果未能解决你的问题,请参考以下文章
Apple Watch Series 6或将增加焦虑监测和睡眠追踪功能
3日 | SteamVR将放弃支持苹果macOS系统;Apple Watch Series 6或将增加焦虑监测和睡眠追踪功能