当应用在 background/suspended 运行 时继续倒计时

Continue countdown timer when app is running in background/suspended

我有一个倒数计时器。

问题是当用户将应用程序置于后台时,我需要继续倒计时。或者暂停应用程序?我不确定描述此操作的正确术语是什么。

换句话说,用户打开应用程序并开始倒计时。用户应该能够使用其他应用程序,但倒计时仍在运行。此外,倒计时应该能够 运行 永远,直到它结束或当用户终止应用程序时 运行。

这是我的定时器的代码:

    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();
}

}

我的建议是取消计时器并在应用进入后台时存储 NSDate。 您可以使用此通知来检测进入后台的应用程序:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseApp", name: UIApplicationDidEnterBackgroundNotification, object: nil)

然后取消定时器并存储日期:

func pauseApp(){
    self.stop() //invalidate timer
    self.currentBackgroundDate = NSDate()
}

使用此通知检测用户回来:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "startApp", name: UIApplicationDidBecomeActiveNotification, object: nil)

然后计算存储日期与当前日期的差异,更新计数器并再次启动计时器:

func startApp(){
    let difference = self.currentBackgroundDate.timeIntervalSinceDate(NSDate())
    self.handler(difference) //update difference
    self.start() //start timer
}

你不应该那样做。 运行ning 计时器使设备的处理器 运行ning 保持全功率运行,这很糟糕。

只允许某些类型的应用程序实际上 运行 从后台无限期编码,例如VOIP 应用程序和音乐应用程序。

几个选项:

  1. 为将来的日期设置本地通知(这将向您的应用发送消息,或者如果它不再 运行ning 则重新启动它。)

  2. 启动计时器时,记录当前的 NSDate。然后,当您的应用程序 returns 进入前台或重新启动时,将当前日期与保存的日期进行比较,计算出已经过去的时间量,并确定您的计时器是否已结束。

试试这个。此代码更新应用程序从 background/suspend 返回并处于活动状态的时间。

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
}

}

确保打开后台模式并在 xCode 中设置所需的值。很奇怪,但即使关闭后台模式,这段代码也能正常工作。在 AppDelegate 中设置 application.beginBackgroundTask {} 后似乎可以使用任何计时器。

我使用这个代码:

在 AppDelegate 中添加以下代码:

func applicationDidEnterBackground(_ application: UIApplication) {

    application.beginBackgroundTask {} // allows to run background tasks
}

然后在下面的地方调用方法。

func registerBackgroundTask() {
    DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + 5, qos: .background) {
            print("fasdf")
        }
}