Использование выполнения (afterDelay :) в цикле while дает логическую ошибку? - PullRequest
0 голосов
/ 08 апреля 2019

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

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

У меня возникают трудности с правильным обратным отсчетом: переменные, используемые для установки файлов изображений h, min1 и min2, устанавливаются в 0 после 1-секундной задержки. Кажется, что цикл while вызывает функцию задержки один раз, выполняет итерацию до тех пор, пока условие не будет выполнено, не задерживая таймер, а затем отображает окончательные значения.

Я пробовал разные способы синхронизации с задержкой в ​​1 секунду, в том числе с использованием подхода let timer = Timer. и DispatchQueue.main., но, похоже, они не работают.

    @IBAction func slider(_ sender: UISlider)
    {
        self.x = Int(sender.value)
        // Note I omitted the rest of this code as it concerned setting images while changing slider value, and used local variables.
    }

    var x: Int = 60
    var h: Int = 1
    var min1: Int = 1
    var min2: Int = 7

    @objc func animate2 ()
    {
        checkLbl.text = String(h) + ("1")
        checkLbl2.text = String(min1) + ("1")
        checkLbl3.text = String(min2) + ("1")
        self.H2ImageView.image = UIImage(named: "\(h).png")!
        self.M1ImageView.image = UIImage(named: "\(min1).png")!
        self.M2ImageView.image = UIImage(named: "\(min2).png")!
    }

    func animate ()
    {
        var timeLeft = x
        var seconds = 60
        while timeLeft >= 0
        {
            (h, _) = x.quotientAndRemainder(dividingBy: 60)
            (min1, min2) = x.quotientAndRemainder(dividingBy: 10)
            if min1 >= 6
            {
                min1 = min1 - 6
                if h == 2
                {
                    min1 = 0
                }
            }
            checkLbl.text = String(h)
            checkLbl2.text = String(min1)
            checkLbl3.text = String(min2)
            // checkLbl used to track the variables
            perform(#selector(animate2), with: nil, afterDelay: 1)
            seconds = seconds - 1
            if seconds == 0
            {
                timeLeft = timeLeft - 1
                seconds = 60
            }
        }
    }

    @IBAction func watchPress(_ sender: UIButton)
    {
        animate()
    }

Подводя итог: я ожидал, что функция задержки будет обновлять h, min1 и min2 (и, следовательно, обновлять текст checkLbl) каждую секунду, однако эти значения идут прямо к 0.

Любая помощь приветствуется, спасибо!

1 Ответ

0 голосов
/ 08 апреля 2019

Вы должны понимать, что performSelector, DispatchQueue.main.asyncAfter и Timer все выполняют код асинхронно после определенного времени. Это означает, что строка B не будет проходить через одну секунду после строки A в этом примере:

checkLbl3.text = String(min2) // A
perform(#selector(animate2), with: nil, afterDelay: 1)
seconds = seconds - 1 // B

Строка B будет работать сразу после строки A. И через одну секунду будет вызван animate2.

Следовательно, вы не должны использовать цикл while. Вместо этого вы должны использовать Timer, например:

Timer(timeInterval: 1, repeats: true) { (timer) in
    if timeLeft == 0 {
        timer.invalidate()
    }
    seconds -= 1
    if seconds == 0
    {
        timeLeft = timeLeft - 1
        seconds = 60
    }
    // update the labels and image views
}

Я также рекомендую хранить только оставшееся время в виде количества секунд. Удалите переменные h, min1, min2 и seconds. Вычисляйте их только из времени, оставшегося после обновления пользовательского интерфейса.

...