I have a functional countdown timer.
我有一个功能倒数计时器。
The problem is that I need to continue the countdown when the user puts the app in the background. Or suspends the app? I am not sure what the correct terminology is to describe this action.
问题是,当用户将应用程序置于后台时,我需要继续倒计时。或暂停应用程序?我不确定描述此操作的正确术语是什么。
In other words, the user opens the app and starts the countdown. The user should be able to use other apps but the countdown still operate. Also, the countdown should be able to run forever until it finishes or when the user terminates running the app.
换句话说,用户打开应用程序并开始倒计时。用户应该能够使用其他应用程序,但倒计时仍然可以运行。此外,倒计时应该能够永久运行,直到完成或用户终止运行应用程序。
Here is the code for my timer:
这是我的计时器的代码:
class Timer{
var timer = NSTimer();
// the callback to be invoked everytime the timer 'ticks'
var handler: (Int) -> ();
//the total duration in seconds for which the timer should run to be set by the caller
let duration: Int;
//the amount of time in seconds elapsed so far
var elapsedTime: Int = 0;
var targetController = WaitingRoomController.self
var completionCallback: (() -> Void)!
/**
:param: an integer duration specifying the total time in seconds for which the timer should run repeatedly
:param: handler is reference to a function that takes an Integer argument representing the elapsed time allowing the implementor to process elapsed time and returns void
*/
init(duration: Int , handler : (Int) -> ()){
self.duration = duration;
self.handler = handler;
}
/**
Schedule the Timer to run every 1 second and invoke a callback method specified by 'selector' in repeating mode
*/
func start(){
self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "onTick", userInfo: nil, repeats: true);
}
/**
invalidate the timer
*/
func stop(){
println("timer was invaidated from stop()")
timer.invalidate();
}
/**
Called everytime the timer 'ticks'. Keep track of the total time elapsed and trigger the handler to notify the implementors of the current 'tick'. If the amount of time elapsed is the same as the total duration for the timer was intended to run, stop the timer.
*/
@objc func onTick() {
//println("onTick")
//increment the elapsed time by 1 second
self.elapsedTime++;
//Notify the implementors of the updated value of elapsed time
self.handler(elapsedTime);
//If the amount of elapsed time in seconds is same as the total time in seconds for which this timer was intended to run, stop the timer
if self.elapsedTime == self.duration {
self.stop();
}
}
deinit{
println("timer was invalidated from deinit()")
self.timer.invalidate();
}
}
3 个解决方案
#1
4
What I suggest is cancel the timer and store a NSDate
when the app goes to the background. You can use this notification to detect the app going to the background:
我建议取消计时器并在应用程序进入后台时存储NSDate。您可以使用此通知来检测应用程序转到后台:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseApp", name: UIApplicationDidEnterBackgroundNotification, object: nil)
Then cancel the timer and store the date:
然后取消计时器并存储日期:
func pauseApp(){
self.stop() //invalidate timer
self.currentBackgroundDate = NSDate()
}
Use this notification to detect the user coming back:
使用此通知检测回来的用户:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "startApp", name: UIApplicationDidBecomeActiveNotification, object: nil)
Then calculate the difference from the stored date to the current date, update your counter and start the timer again:
然后计算从存储日期到当前日期的差异,更新计数器并再次启动计时器:
func startApp(){
let difference = self.currentBackgroundDate.timeIntervalSinceDate(NSDate())
self.handler(difference) //update difference
self.start() //start timer
}
#2
2
You shouldn't do that. A running timer keeps the device's processor running at full power, which is bad.
你不应该这样做。正在运行的计时器可使设备的处理器以全功率运行,这很糟糕。
Only certain types of apps are allowed to actually run code indefinitely from the background, e.g. VOIP apps and music apps.
只允许某些类型的应用程序从后台无限期地实际运行代码,例如VOIP应用和音乐应用。
A couple of options:
有两种选择:
-
Set up a local notification for a future date (which will send a message to your app, or re-launch it if it was no longer running.)
设置未来日期的本地通知(将向您的应用发送消息,或者如果不再运行则重新启动它)。
-
When you start your timer, record the current NSDate. Then, when your app returns to the foreground or gets re-launched, fetch compare the current date to the saved date, figure out the amount of time that's elapsed, and decide if your timer is finished yet.
启动计时器时,记录当前的NSDate。然后,当您的应用程序返回到前台或重新启动时,获取将当前日期与保存的日期进行比较,计算已经过的时间量,并确定您的计时器是否已完成。
#3
0
Try this instead.. this code updates the time when app comes back from background/suspend and in active state..
请尝试这个..此代码更新应用程序从后台/暂停和处于活动状态时返回的时间。
class ProgressV: UIView {
class ProgressV:UIView {
var timer: NSTimer!
var expirationDate = NSDate()
//Set the time that you want to decrease..
var numSeconds: NSTimeInterval = 36000.0 // Ex:10:00 hrs
func startTimer()
{
// then set time interval to expirationDate…
expirationDate = NSDate(timeIntervalSinceNow: numSeconds)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(updateUI(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
}
func updateUI(timer: NSTimer)
{
// Call the currentTimeString method which can decrease the time..
let timeString = currentTimeString()
labelName.text = timeString
}
func currentTimeString() -> String {
let unitFlags: NSCalendarUnit = [.Hour, .Minute, .Second]
let countdown: NSDateComponents = NSCalendar.currentCalendar().components(unitFlags, fromDate: NSDate(), toDate: expirationDate, options: [])
var timeRemaining: String
if countdown.hour > 0
{
timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
}
else {
timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
}
return timeRemaining
}
}
#1
4
What I suggest is cancel the timer and store a NSDate
when the app goes to the background. You can use this notification to detect the app going to the background:
我建议取消计时器并在应用程序进入后台时存储NSDate。您可以使用此通知来检测应用程序转到后台:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseApp", name: UIApplicationDidEnterBackgroundNotification, object: nil)
Then cancel the timer and store the date:
然后取消计时器并存储日期:
func pauseApp(){
self.stop() //invalidate timer
self.currentBackgroundDate = NSDate()
}
Use this notification to detect the user coming back:
使用此通知检测回来的用户:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "startApp", name: UIApplicationDidBecomeActiveNotification, object: nil)
Then calculate the difference from the stored date to the current date, update your counter and start the timer again:
然后计算从存储日期到当前日期的差异,更新计数器并再次启动计时器:
func startApp(){
let difference = self.currentBackgroundDate.timeIntervalSinceDate(NSDate())
self.handler(difference) //update difference
self.start() //start timer
}
#2
2
You shouldn't do that. A running timer keeps the device's processor running at full power, which is bad.
你不应该这样做。正在运行的计时器可使设备的处理器以全功率运行,这很糟糕。
Only certain types of apps are allowed to actually run code indefinitely from the background, e.g. VOIP apps and music apps.
只允许某些类型的应用程序从后台无限期地实际运行代码,例如VOIP应用和音乐应用。
A couple of options:
有两种选择:
-
Set up a local notification for a future date (which will send a message to your app, or re-launch it if it was no longer running.)
设置未来日期的本地通知(将向您的应用发送消息,或者如果不再运行则重新启动它)。
-
When you start your timer, record the current NSDate. Then, when your app returns to the foreground or gets re-launched, fetch compare the current date to the saved date, figure out the amount of time that's elapsed, and decide if your timer is finished yet.
启动计时器时,记录当前的NSDate。然后,当您的应用程序返回到前台或重新启动时,获取将当前日期与保存的日期进行比较,计算已经过的时间量,并确定您的计时器是否已完成。
#3
0
Try this instead.. this code updates the time when app comes back from background/suspend and in active state..
请尝试这个..此代码更新应用程序从后台/暂停和处于活动状态时返回的时间。
class ProgressV: UIView {
class ProgressV:UIView {
var timer: NSTimer!
var expirationDate = NSDate()
//Set the time that you want to decrease..
var numSeconds: NSTimeInterval = 36000.0 // Ex:10:00 hrs
func startTimer()
{
// then set time interval to expirationDate…
expirationDate = NSDate(timeIntervalSinceNow: numSeconds)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(updateUI(_:)), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
}
func updateUI(timer: NSTimer)
{
// Call the currentTimeString method which can decrease the time..
let timeString = currentTimeString()
labelName.text = timeString
}
func currentTimeString() -> String {
let unitFlags: NSCalendarUnit = [.Hour, .Minute, .Second]
let countdown: NSDateComponents = NSCalendar.currentCalendar().components(unitFlags, fromDate: NSDate(), toDate: expirationDate, options: [])
var timeRemaining: String
if countdown.hour > 0
{
timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
}
else {
timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
}
return timeRemaining
}
}