代码之家  ›  专栏  ›  技术社区  ›  rustyMagnet

Swift didSet已调用但未更新UILabel-iOS属性观察器

  •  0
  • rustyMagnet  · 技术社区  · 6 年前

    didSet label.text = String(counter) 似乎什么也没做。

    斯威夫特5

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var label: UILabel!
    
        var counter:Int = 0 {
            didSet {
                print("old value \(oldValue) and new value: \(counter)")
                label.text = String(counter)
                sleep(1).  // just added to show the label.text is not updating
            }
        }
    
        @IBAction func start_btn(_ sender: Any) {
            for _ in 1...3 {
                counter += 1
            }
        }
    }
    

    滴漏 SwiftUI ).

    滴漏 代码被调用。

    old value 0 and new value: 1. Main thread: true
    old value 1 and new value: 2. Main thread: true
    old value 2 and new value: 3. Main thread: true
    
    3 回复  |  直到 6 年前
        1
  •  1
  •   Mohammad Sadiq    6 年前

    你可以做到如下

    @IBAction func start_btn(_ sender: Any) {
        updateCounter()
    }
    
    func updateCounter() {
        if counter == 3 {
            return
        } else {
            counter += 1
            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
                self.updateCounter()
            })
    
        }
    }
    
        2
  •  1
  •   shim    6 年前

    你好像在做一个从0开始到3结束的计数器。如果是这样的话,你不应该打电话给 sleep

    编辑:显然 睡觉 添加呼叫是为了演示? 在任何情况下,标签似乎只在计数完成时更新的原因是 for 循环运行太快,用户界面无法在每个循环上更新 counter

    而是使用 Timer :

    counter = 0
    let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
        self.counter += 1
    
        if self.counter >= 3 {
            timer.invalidate()
        }
    }
    

    你也可以 DispatchQueue.main.asyncAfter :

    func countUp() {
        guard counter < 3 else { return }
    
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.counter += 1
            fire()
        }
    }
    

    在很短的时间间隔内,这两种方法之间的差异将是微不足道的。为了精确地计算时间,我们也不应该依赖它,而应该使用 Date 计时器 这将每十分之一秒触发一次,并通过舍入到最近的秒(例如)来更新计数器。

        3
  •  0
  •   Dávid Pásztor    6 年前

    从不,从不打电话 sleep 在iOS应用程序中。这将阻塞主线程,这意味着您的应用程序将被冻结整整一秒钟 sleep(1) .

    这意味着当循环进入时,主线程将被阻塞 start_btn 完成,因此只能在循环完成后更新UI。

    @IBAction func start_btn(_ sender: Any) {
        for i in 1...3 {
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(i), execute: {
                self.counter += 1
            })
        }
    }
    

    并移除 睡眠(1) didSet