SwiftUI 在滚动列表期间停止更新

Kev*_*n R 3 swiftui combine

给定 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)

将导致这种行为:

结果视频

Asp*_*eri 5

这是由于自然TimerRunLoop。改用 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)


rob*_*off 5

您可以在 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)