[英]Swift didSet called but not updating UILabel - iOS Property observer
知道为什么我的 label.text 仅在计数完成时更新吗?
didSet
被调用。 但是label.text = String(counter)
似乎什么也没做。
Swift 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
}
}
}
从主线程调用didSet
代码。 它与故事板(不是SwiftUI
)正确连接。
可以看到调用了didSet
代码。
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
看起来您正在尝试创建某种从 0 开始并在 3 停止的计数器。如果是这种情况,您不应该调用sleep
(这会阻塞主线程)。
编辑:显然sleep
呼叫是为了演示目的而添加的? 在任何情况下,您的 label 似乎只在计数完成时才更新的原因是因为for
循环运行得太快,UI 无法在每个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
和一个Timer
,它每十分之一秒触发一次,并通过四舍五入到最接近的秒来更新计数器(例如)。
你可以像下面这样实现它
@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()
})
}
}
永远不要在 iOS 应用程序中调用sleep
。 这将阻塞主线程,这意味着您的应用程序将被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
})
}
}
并从didSet
中删除sleep(1)
。
为避免 UI 阻塞,将整个例程分派到全局队列,并将 UI 部分分派到主队列。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
var counter:Int = 0 {
didSet {
print("old value \(oldValue) and new value: \(counter)")
DispatchQueue.main.async {
self.label.text = String(self.counter)
}
sleep(1)
}
}
@IBAction func start_btn(_ sender: Any) {
DispatchQueue.global().async {
for _ in 1...3 {
self.counter += 1
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.