[英]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.