在后台迅速的NSTimer [英] swift NSTimer in Background

查看:246
本文介绍了在后台迅速的NSTimer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我所遇到的许多问题与如何在后台处理的NSTimer这里堆栈或其他地方。我试过的,其实是有道理的所有选项之一。要停止计时器,当应用程序被切换到后台与

I have come across a lot of issues with how to handle NSTimer in background here on stack or somewhere else. I've tried one of all the options that actually made sense .. to stop the timer when the application goes to background with

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

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "appDidBecomeActive", name: UIApplicationWillEnterForegroundNotification, object: nil)

起初,我以为,我的问题解决了,我刚保存的时候,应用程序做进入的背景和计算的差异,当应用程序进入前景..但后来我发现的时候实际上是由3,4延期, 5秒..它实际上是不一样的。我已经将它比作另一台设备上的秒表。

At first I thought that my problem is solved, I just saved the time when the app did enter background and calculated the difference when the app entered foreground .. but later I noticed that the time is actually postponed by 3, 4 , 5 seconds .. that it actually is not the same .. I've compared it to the stopwatch on another device.

,是否确有固溶在后台运行的NSTimer?

Is there REALLY any SOLID solution to running an NSTimer in background?

推荐答案

您不应该基于其进入时的背景或继续,而只是保存你是从或计算时间在任何调整搞乱(视取决于您是否正在或计数下降)。然后,当应用程序再次启动时,您只需重建计时器时,使用从/时间。

You shouldn't be messing with any adjustments based upon when it enters background or resumes, but rather just save the time that you are counting from or to (depending upon whether you are counting up or down). Then when the app starts up again, you just use that from/to time when reconstructing the timer.

同样,请确保您的计时器处理程序不依赖于精确定时的处理选择器称为(例如做的的不喜欢秒++什么之类的东西,因为它可能不叫,当你希望它会precisely),但经常回从/时间。

Likewise, make sure your timer handler is not dependent upon the exact timing that the handling selector is called (e.g. do not do anything like seconds++ or anything like that because it may not be called precisely when you hope it will), but always go back to that from/to time.

下面是一个倒计时器,这说明我们不算任何事情的一个例子。我们也不关心 appDidEnterBackground 之间的时间间隔和 appDidBecomeActive 。只需保存的停止时间,然后计时器处理程序只是比较目标停止时间和当前时间,并显示所经过的时间,但是你会喜欢的。

Here is an example of a count-down timer, which illustrates that we don't "count" anything. Nor do we care about the time elapsed between appDidEnterBackground and appDidBecomeActive. Just save the stop time and then the timer handler just compares the target stopTime and the current time, and shows the elapsed time however you'd like.

private let stopTimeKey = "stopTimeKey"

class ViewController: UIViewController {

    @IBOutlet weak var datePicker: UIDatePicker!
    @IBOutlet weak var timerLabel: UILabel!

    var stopTime: NSDate?

    override func viewDidLoad() {
        super.viewDidLoad()

        registerForLocalNotifications()

        stopTime = NSUserDefaults.standardUserDefaults().objectForKey(stopTimeKey) as? NSDate
        if let time = stopTime {
            if time.compare(NSDate()) == .OrderedDescending {
                startTimer(time)
            } else {
                notifyTimerCompleted()
            }
        }
    }

    func registerForLocalNotifications() {
        let types: UIUserNotificationType = [.Badge, .Sound, .Alert]

        let settings = UIUserNotificationSettings(forTypes: types, categories: nil)

        UIApplication.sharedApplication().registerUserNotificationSettings(settings)
    }

    @IBAction func didTapStartButton(sender: AnyObject) {
        let time = datePicker.date
        if time.compare(NSDate()) == .OrderedDescending {
            startTimer(time)
        } else {
            timerLabel.text = "timer date must be in future"
        }
    }

    // MARK: Timer stuff

    var timer: NSTimer?

    func startTimer(stopTime: NSDate) {
        // save `stopTime` in case app is terminated

        NSUserDefaults.standardUserDefaults().setObject(stopTime, forKey: stopTimeKey)
        self.stopTime = stopTime

        // start NSTimer

        timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "handleTimer:", userInfo: nil, repeats: true)

        // start local notification (so we're notified if timer expires while app is not running)

        let notification = UILocalNotification()
        notification.fireDate = stopTime
        notification.alertBody = "Timer finished!"
        UIApplication.sharedApplication().scheduleLocalNotification(notification)
    }

    func stopTimer() {
        timer?.invalidate()
        timer = nil
    }

    let dateComponentsFormatter: NSDateComponentsFormatter = {
        let _formatter = NSDateComponentsFormatter()
        _formatter.allowedUnits = [.Hour, .Minute, .Second]
        _formatter.unitsStyle = .Positional
        _formatter.zeroFormattingBehavior = .Pad
        return _formatter
    }()

    // I'm going to use `NSDateComponentsFormatter` to update the 
    // label. Update it any way you want, but the key is that
    // we're just using the scheduled stop time and the current
    // time, but we're not counting anything. If you don't want to 
    // use `NSDateComponentsFormatter`, I'd suggest considering
    // `NSCalendar` method `components:fromDate:toDate:options:` to 
    // get the number of hours, minutes, seconds, etc. between two 
    // dates.

    func handleTimer(timer: NSTimer) {
        let now = NSDate()

        if stopTime!.compare(now) == .OrderedDescending {
            timerLabel.text = dateComponentsFormatter.stringFromDate(now, toDate: stopTime!)
        } else {
            stopTimer()
            notifyTimerCompleted()
        }
    }

    func notifyTimerCompleted() {
        timerLabel.text = "Timer done!"
    }

}

顺便说一下,上面还说明了如何使用一个本地通知的(在情况而该应用当前未运行计时器过期)。

By the way, the above also illustrates the use of a local notification (in case the timer expires while the app isn't currently running).

这篇关于在后台迅速的NSTimer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆