SwiftUI 使用高频数据更新 UI

Mur*_*ill 3 mvvm grand-central-dispatch observable swiftui combine

我正在尝试使用来自单独后台线程的高频数据来更新主视图。我创建了两个选项卡视图,如果更新速度慢,我可以更改视图。但在另一种情况下,用户界面没有反应。我只在真实设备上观察到这种行为,在模拟器中一切正常。

while循环仍然代表一个imu,只是为了简单起见。

有人知道如何解决这个问题吗?

非常感谢!

import SwiftUI

struct ContentView: View {
    
    @EnvironmentObject var loop : Loop
    
    var body: some View {
        
        TabView{
        
            VStack {
                Text("Content View")
                LoopView()
            }.tabItem{
                  VStack{
                      Text("tab1")
                      Image(systemName: "car")
                }
                
            }
            
            Text("second view").tabItem{
                                    VStack{
                                        Text("tab2")
                                        Image(systemName: "star")
                }
            }
        }
    }
}


class Loop : ObservableObject {
    
    @Published var i : Int
    
    func startLoop() {
        while true {
            print("i = \(self.i)")
            DispatchQueue.main.async {
                self.i += 1
            }

            //sleep(1) // comment out to simulate worst case
        }
    }
    
    init() {
        DispatchQueue.global(qos: .background).async {
            self.startLoop()
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 7

您需要将频率数据的更新存储与表示的 UI 部分分开,以便存储接收/包含实际的真实数据,但 UI 部分会在您需要时立即更新(0.5 秒、1 秒、5 秒等)

这是可能的方法。使用 Xcode 12 / iOS 14 进行测试。

import Combine
class Loop : ObservableObject {
    private var storage: Int = 0
    private var counter = PassthroughSubject<Int, Never>()

    @Published var i : Int = 0   // only for UI

    func startLoop() {
        while true {
            storage += 1     // update storage 
            counter.send(storage) // publish event
        }
    }

    private var subscriber: AnyCancellable?
    init() {
        subscriber = counter
            .throttle(for: 0.5, scheduler: DispatchQueue.global(qos: .background), latest: true) // drop in background
            .receive(on: DispatchQueue.main)  // only latest result
            .sink { [weak self] (value) in    // on @pawello2222 comment
               self?.i = value
            }

        DispatchQueue.global(qos: .background).async {
            self.startLoop()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 建议对类使用“sink(receiveValue:)”以避免循环引用。请参阅 https://forums.swift.org/t/does-assign-to- Produce-memory-leaks/29546 (2认同)