在while循环中使用perform(afterDelay :)给我一个逻辑错误?

时间:2019-04-08 08:34:28

标签: swift loops delay

我正在制作一个计时器程序,该程序使用滑块设置计时器值,然后更新数字时钟显示以将剩余的相应数字时间显示为图像文件。

我试图在while循环中运行一个1秒的延迟功能,并每秒更新一次图像文件,本质上是试图创建一个计时器,该计时器更新用于确定实时使用哪些图像的变量。

我很难正确分配倒数:延迟一秒钟后,用于设置图像文件hmin1min2的变量设置为0 。看来,while循环只调用一次delay函数,然后在不延迟计时器的情况下进行迭代直至满足条件,然后显示最终值。

我尝试了计时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()
    }

总结一下:我希望延迟函数每秒更新一次hmin1min2(并因此更新checkLbl文本),但是这些值会直接变为0

感谢您的帮助,谢谢!

1 个答案:

答案 0 :(得分:0)

您需要了解,performSelectorDispatchQueue.main.asyncAfterTimer在一定时间后都会异步运行代码。这意味着在此示例中,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
}

我还建议您仅将剩余时间存储为秒数。删除您的hmin1min2seconds变量。仅从更新UI时剩余的时间开始计算它们。