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

Posted

技术标签:

【中文标题】SwiftUI 在列表滚动期间停止更新【英文标题】:SwiftUI stops updates during scrolling of List 【发布时间】:2020-04-02 09:15:08 【问题描述】:

在 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)
            
        
    

将导致这种行为:

【问题讨论】:

我不确定,但我认为 SwiftUI 不可能。它可能适用于 UIKit... 【参考方案1】:

您可以像 Asperi 的回答那样使用 GCD,但这并不能解释为什么您的代码不起作用。

问题在于,当滚动视图跟踪您的触摸时,它以.tracking 模式运行运行循环。但是因为您使用scheduledTimer(withTimeInterval:repeats:block:) 创建了Timer,所以Timer 仅设置为在.default 模式下运行。

您可以像这样将计时器添加到所有常见的运行循环模式(包括.tracking):

RunLoop.main.add(timer, forMode: .common)

但我可能会改用 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)
    

【讨论】:

【参考方案2】:

这是由于TimerRunLoop 的性质。改用 GCD,如下面的方法

init() 
    var runner: (() -> ())?
    runner = 
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)  [weak self] in
            if let self = self 
                self.update()
                runner?()
            
        
    
    runner?()

【讨论】:

谢谢!非常有用的知识!

以上是关于SwiftUI 在列表滚动期间停止更新的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 选择器在 ObservedObject 公共变量更新期间滚动时跳转

滚动时更新自定义列表视图失败

当 Swiftui Picker 滚动其列表时触发的事件

在 SwiftUI ScrollView 中停止垂直滚动

应用`listRowInsets`时SwiftUI列表未更新

SwiftUI - 在 Digital Crown 旋转期间显示视图