Arhn - архитектура программирования

Swift: другой таймер запускается, когда приложение переходит в фоновый режим

Я изо всех сил пытался понять это в течение нескольких дней: мне нужно создать таймер, который пользователь не может убить, поэтому, как только они запустят его, даже если они убьют приложение или оно войдет в фоновый режим, он подберет, где он остановился, и для этого я сохраняю дату завершения работы приложения, а затем вычисляю разницу, так что эта часть работает нормально.

Однако проблема, с которой я продолжаю сталкиваться, заключается в том, что второй таймер, похоже, запускается, если я сворачиваю приложение и возвращаю его обратно. Я не уверен, как таймеры управляются в фоновом режиме, но ничего из того, что я пробовал (вызов timer.invalidate() при вызове applicationWillResignActive, вызов его в deinit() ), похоже, не работает. Поведение, которое я вижу после этого, заключается в том, что таймер будет считать так: 80 - 78 - 79 - 76 - 77..

Существует также проблема, когда таймер иногда превышает время, в течение которого он должен работать после закрытия приложения, но я не могу найти точную причину этого, потому что это происходит не всегда.

Любая идея, что я делаю неправильно?

Большое спасибо.

class Focus: UIViewController {

// MARK: Variables
var timer = Timer()
let timeToFocus = UserDefaults.standard.double(forKey: "UDTimeToFocus")
let currentFocusedStats = UserDefaults.standard.integer(forKey: "UDFocusStats")


// MARK: Outlets
@IBOutlet weak var progress: KDCircularProgress!
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var focusTimeLabel: UILabel!
@IBOutlet weak var stepNameLabel: UILabel!
@IBOutlet weak var focusAgain: UIButton!
@IBOutlet weak var allDone: UIButton!
@IBOutlet weak var help: UIButton!
@IBOutlet weak var dottedCircle: UIImageView!


// MARK: Outlet Functions
@IBAction func helpTU(_ sender: Any) { performSegue(withIdentifier: "ToFocusingHelp", sender: nil) }
@IBAction func helpTD(_ sender: Any) { help.tap(shape: .rectangle) }


@IBAction func allDoneTU(_ sender: Any) {
    UserDefaults.standard.set(false, forKey: "UDFocusIsRunning")
    UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
    completeSession()
    hero(destination: "List", type: .zoomOut)
}

@IBAction func allDoneTD(_ sender: Any) { allDone.tap(shape: .rectangle) }


@IBAction func focusAgainTU(_ sender: Any) {
    UserDefaults.standard.set(currentFocusedStats + Int(timeToFocus), forKey: "UDFocusStats")
    UserDefaults.standard.set(true, forKey: "UDShouldStartFocus")
    initFocus()
}

@IBAction func focusAgainTD(_ sender: Any) { focusAgain.tap(shape: .rectangle) }


// MARK: Class Functions
@objc func initFocus() {

    var ticker = 0.0
    var angle = 0.0
    var duration = 0.0

     if UserDefaults.standard.bool(forKey: "UDShouldStartFocus") == true {
        UserDefaults.standard.set(Date(), forKey: "UDFocusStartDate")
        UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
        ticker = timeToFocus
        duration = timeToFocus
        angle = 0.0
        print("starting")
     } else {
        let elapsedTime = difference(between: UserDefaults.standard.object(forKey: "UDFocusStartDate") as! Date, and: Date())
        let timeLeft = timeToFocus - elapsedTime
        ticker = timeLeft
        duration = timeLeft
        angle = elapsedTime / (timeToFocus / 360)
     }

    // Timer
    let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
        if ticker > 0 {
            self.timeLabel.text = "\(Int(ticker))s"
            ticker -= 1
        }
    }
    timer.fire()

    // Progress Circle
    progress.animate(fromAngle: angle, toAngle: 360, duration: duration) { completed in
        if completed { self.completeSession() }
    }


    // UI Changes
    allDone.isHidden = true
    focusAgain.isHidden = true
    help.isHidden = false
}


func completeSession() {
    // The timer gets fired every time, but this will invalidate it if it's complete
    timer.invalidate()
    timeLabel.text = "Done"
    help.isHidden = true
    allDone.isHidden = false
    focusAgain.isHidden = false
}


// MARK: viewDidLoad
override func viewDidLoad() {

    initFocus()

    allDone.isHidden = true
    focusAgain.isHidden = true

    if timeToFocus < 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60)) minutes" }
    else if timeToFocus == 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hour" }
    else { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hours" }

    stepNameLabel.text = UserDefaults.standard.string(forKey: "UDSelectedStep")

    // This resumes the timer when the user sent the app in the background.
    NotificationCenter.default.addObserver(self, selector: #selector(self.initFocus), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.fadeProgress), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)

}

@objc func fadeProgress(){

    // This function is called both when the view will enter foreground (for waking the phone or switching from another app) and on viewWillAppear (for starting the app fresh). It will fade the progress circle and buttons to hide a flicker that occurs.
    timeLabel.alpha = 0
    dottedCircle.alpha = 0
    progress.alpha = 0
    allDone.alpha = 0
    focusAgain.alpha = 0

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
        UIButton.animate(withDuration: 0.5, animations: {
            self.timeLabel.alpha = 1
            self.dottedCircle.alpha = 1
            self.progress.alpha = 1
            self.allDone.alpha = 1
            self.focusAgain.alpha = 1

        })
    })
}

// MARK: viewWillAppear
override func viewWillAppear(_ animated: Bool) { fadeProgress() }
}
30.07.2019

Ответы:


1

Кажется, проблема в том, что вы создаете локальную переменную timer внутри initFocus(), но вызываете invalidate внутри completeSession для другой timer, определенной там:

class Focus: UIViewController {

// MARK: Variables
var timer = Timer()
30.07.2019
  • Эй, @Вячеслав, это имеет большой смысл, на самом деле я этого не знал. completeSession вызывается, когда прогресс завершается или когда нажимается кнопка, поэтому это не приведет к аннулированию таймера, когда приложение переходит в фоновый режим. Должен ли я аннулировать его, когда я получаю уведомление о том, что приложение перейдет в фоновый режим, или есть лучший способ? 31.07.2019
  • Просто измените let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { на timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { 31.07.2019
  • Большое спасибо за объяснение, это, кажется, исправлено. 31.07.2019
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..