簡體   English   中英

當應用程序不是焦點時,運行一個在后台倒數的計時器嗎? 迅速

[英]Running a Timer that counts down in background when app isnt the focus? Swift

我希望我的倒數計時器暫停運行,然后在應用離開/返回焦點時恢復計時,利用這段時間來計算應該減去多少時間。

我正在使用應用程序委托文件(不確定這是正確的位置嗎?或者它們是否應作為自身功能存在於視圖控制器文件中?)

問題是即時通訊出現很多錯誤,例如:

類型“ AppDelegate”的值沒有成員“ restTimer”

使用未解析的標識符“ nextFireDate”

使用未解決的標識符“選擇器”

在我的視圖控制器文件中將restTimer聲明為計時器,但是當我在該文件中嘗試這些塊時,未解析的標識符出現了相同數量的錯誤

並使用以下2個代碼塊

    func applicationWillResignActive(_ application: UIApplication) {
        guard let t = self.restTimer else { return }
        nextFireDate = t.fireDate
        t.invalidate()

func applicationDidBecomeActive(_ application: UIApplication) {
        guard let n = nextFireDate else { return }
        let howMuchLonger = n.timeIntervalSinceDate(NSDate())
        if howMuchLonger < 0 {
            print("Should have already fired \(howMuchLonger) seconds ago")
            target!.performSelector(selector!)
        } else {
            print("should fire in \(howMuchLonger) seconds")
            Timer.scheduledTimerWithTimeInterval(howMuchLonger, target: target!, selector: selector!, userInfo: nil, repeats: false)
        }
}

更新:由於包含答案的問題,添加了完整視圖代碼

import Foundation
import UIKit

class RestController: UIViewController {

    @IBOutlet weak var restRemainingCountdownLabel: UILabel!
    @IBOutlet weak var setsRemainingCountdownLabel: UILabel!
    @IBOutlet weak var numberOfSetsLabel: UILabel!
    @IBOutlet weak var numberOfRestLabel: UILabel!
    @IBOutlet weak var adjustSetsStepper: UIStepper!
    @IBOutlet weak var adjustRestStepper: UIStepper!

    var startDate: Date!
    let startDateKey = "start.date"
    let interval = TimeInterval(20)

    var restTimer: Timer!
    var restCount = 0
    var setCount = 0
    var selectedTime = 1
    var selectedSets = 1

    private let resignDateKey = "resign.date"

    @IBAction func endSetPressed(_ sender: Any) {
        if (setCount > 0){
            setCount -= 1
            setsRemainingCountdownLabel.text = String(setCount)
        }
        handleTimer()
    }

    @IBAction func setStepperValueChanged(_ sender: UIStepper) {
        numberOfSetsLabel.text = Int(sender.value).description
        self.setCount = Int(sender.value)
        self.selectedSets = setCount
        setsRemainingCountdownLabel.text = String(setCount)
    }

    @IBAction func restStepperValueChanged(_ sender: UIStepper) {
        numberOfRestLabel.text = Int(sender.value).description
        let timeMinSec = timeFormatted(totalSeconds: Int(sender.value)*60)
        restRemainingCountdownLabel.text = timeMinSec
        self.selectedTime = Int(sender.value)
        restCount = self.selectedTime * 60
    }

    @IBAction func resetSetsButton(_ sender: Any) {
        setCount = Int(adjustSetsStepper.value)
        setsRemainingCountdownLabel.text = String(setCount)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        numberOfSetsLabel.text = String(selectedSets)
        numberOfRestLabel.text = String(selectedTime)

        createTimer(interval: interval)
        NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc private func willResignActive(notification: Notification) {
        print("resigning")
        guard restTimer.isValid else {
            UserDefaults.standard.removeObject(forKey: startDateKey)
            return
        }
        restTimer.invalidate()
        UserDefaults.standard.set(Date(), forKey: startDateKey)
    }

    @objc private func didBecomeActive(notification: Notification) {
        print("resume")
        if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
            let elapsed = -startDate.timeIntervalSinceNow
            print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
            if elapsed > interval {
                timerUp()
            } else {
                createTimer(interval: interval - elapsed)
            }
        }
    }

    private func createTimer (interval: TimeInterval) {
        restTimer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
            self?.timerUp()
        }
        startDate = Date()
    }

    private func timerUp() {
        print("At least \(interval) seconds has elapsed")
    }


    func handleSets() {

        if (setCount > 0) {

            self.restCount = self.selectedTime * 60
        }
        handleTimer()
    }

    func handleTimer() {

        if (restTimer?.isValid ?? false) {

            restTimer?.invalidate()
            restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)

        } else {

            restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)
        }
    }

    func updateTimer() {
        if (restCount > 0){
            restCount -= 1
        } else if (restCount == 0){
            restTimer?.invalidate()

        }
        restRemainingCountdownLabel.text = timeFormatted(totalSeconds: restCount)
    }

    func timeFormatted(totalSeconds: Int) -> String {
        let seconds: Int = totalSeconds % 60
        let minutes: Int = (totalSeconds / 60) % 60
        return String(format: "%02d:%02d", minutes, seconds)
    }

您不必為此使用AppDelegate,因為它還會發布通知。 您可以根據需要使用AppDelegate。 這是使用通知的代碼:

    class ViewController: UIViewController{
        private let startDateKey = "start.date"
        private let interval = TimeInterval(20)
        private var startDate: Date!
        private var timer: Timer!
        override func viewDidLoad() {
            super.viewDidLoad()
            createTimer(interval: interval)
            NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
        }

        deinit {
            NotificationCenter.default.removeObserver(self)
        }

        @objc private func willResignActive(notification: Notification) {
            print("resigning")
            guard timer.isValid else {
                UserDefaults.standard.removeObject(forKey: startDateKey)
                return
            }
            timer.invalidate()
            UserDefaults.standard.set(Date(), forKey: startDateKey)
        }

        @objc private func didBecomeActive(notification: Notification) {
            print("resume")
            if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
                let elapsed = -startDate.timeIntervalSinceNow
                print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
                if elapsed > interval {
                    timerUp()
                } else {
                    createTimer(interval: interval - elapsed)
                }
            }
        }

        private func createTimer (interval: TimeInterval) {
            timer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
                self?.timerUp()
            }
            startDate = Date()
        }

        private func timerUp() {
            print("At least \(interval) seconds has elapsed")
        }

    }

我認為您不能依賴於該應用程序在后台時仍保留在內存中這一事實。 因此,您應該存檔在確切時間重新創建計時器所需的所有數據。

例如,在applicationWillResignActive

UserDefaults.standard.set(value: t.nextFireDate forKey:"NextFireDate")

和在applicationWillEnterForegroundapplicationWillEnterForeground

if let fireDate = UserDefaults.standard.object(forKey: "NextFireDate") {
    // setup a timer with the correct fire date
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM