给定 SwiftUI 中的列表,一旦开始平移,列表中视图的更新似乎会暂停,直到滚动停止。有没有办法防止这种情况?
考虑以下代码:
class Model: ObservableObject, Identifiable {
@Published var offset: CGFloat = 0
let id = UUID()
private var timer: Timer!
init() {
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in
self.update()
})
}
func update() {
offset = CGFloat.random(in: 0...300)
}
}
struct ContentView: View {
@ObservedObject var model1 = Model()
@ObservedObject var model2 = Model()
@ObservedObject var model3 = Model()
@ObservedObject var model4 = Model()
var body: some View {
List {
ForEach([model1, model2, model3, model4]) {
Rectangle()
.foregroundColor(.red)
.frame(width: $0.offset, height: 30, alignment: .center)
.animation(.default)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
将导致这种行为:
这是由于自然Timer和RunLoop。改用 GCD,就像下面的方法

init() {
var runner: (() -> ())?
runner = {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
if let self = self {
self.update()
runner?()
}
}
}
runner?()
}
Run Code Online (Sandbox Code Playgroud)
您可以在 Asperi 的回答中使用 GCD,但这并不能解释为什么您的代码不起作用。
问题是,当滚动视图跟踪你的触摸时,它在.tracking模式下运行运行循环。但是因为您创建了Timerusing scheduledTimer(withTimeInterval:repeats:block:),所以Timer仅设置为在该.default模式下运行。
您可以将计时器添加到所有常见的运行循环模式(包括.tracking),如下所示:
RunLoop.main.add(timer, forMode: .common)
Run Code Online (Sandbox Code Playgroud)
但我可能会改用一个 Combine 发布者,就像这样:
class Model: ObservableObject, Identifiable {
@Published var offset: CGFloat = 0
let id = UUID()
private var tickets: [AnyCancellable] = []
init() {
Timer.publish(every: 0.5, on: RunLoop.main, in: .common)
.autoconnect()
.map { _ in CGFloat.random(in: 0...300) }
.sink { [weak self] in self?.offset = $0 }
.store(in: &tickets)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
330 次 |
| 最近记录: |