Mvvm 使用高频数据快速更新用户界面

Mvvm 使用高频数据快速更新用户界面,mvvm,swiftui,observable,grand-central-dispatch,combine,Mvvm,Swiftui,Observable,Grand Central Dispatch,Combine,我试图用来自不同背景线程的高频数据更新主视图。 我已经创建了两个选项卡,如果更新速度慢,我可以更改视图。但在另一种情况下,用户界面没有反应。我只在真实设备上观察到这种行为,在模拟器中一切正常 while循环仍然表示imu,只是为了简单起见 有人知道如何解决这个问题吗 非常感谢 import SwiftUI struct ContentView: View { @EnvironmentObject var loop : Loop var body: some

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

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()
    }
}

你试过用联合收割机解决那个问题吗? 例如,如果用户界面更新太快,您可以在一种缓冲区中收集联合收割机中的一些数据,然后在缓冲区已满后,您可以将其推送到视图中。另一种方法是对输入进行特定延迟的去抖动处理,然后只将最后一次更新推送到视图中。例如,假设您有一个已发布的变量i,它不断更新它的值。您可以引入以下viewmodel:

class Loop: ObservableObject {
    @Published var i: Int = 0
    @Published var updatedVariable: Int = 0
    private var bag = Set<AnyCancellable>()

    init() {
        $i
          .debounce(for: 0.5, scheduler: DispatchQueue.main)
          .sink { [weak self] value in 
              self?.updatedVariable = value
          }
          .store(in: &bag)
        startLoop()
    }

    func startLoop() {
        while true {
            print("i = \(self.i)")
            DispatchQueue.main.async {
                self.i += 1
            }
        }
    }
}
类循环:ObserveObject{
@已发布变量i:Int=0
@已发布的变量updatedVariable:Int=0
私有变量包=集合()
init(){
$i
.debounce(对于:0.5,调度程序:DispatchQueue.main)
.sink{[弱自我]值在
self?.updatedVariable=值
}
.存储(在:&袋中)
startoop()
}
func startoop(){
虽然是真的{
打印(“i=\(self.i)”)
DispatchQueue.main.async{
self.i+=1
}
}
}
}

通过这种方式,如果使用updatedVariable而不是i变量,则只需每0.5秒更新一次ui。

您需要将频率数据的更新存储与表示的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()
        }
    }
}
导入联合收割机
类循环:observeObject{
专用变量存储:Int=0
私有变量计数器=PassthroughSubject()
@已发布的变量i:Int=0//仅适用于UI
func startoop(){
虽然是真的{
存储+=1//更新存储
counter.send(存储)//发布事件
}
}
私有var订户:是否可以取消?
init(){
订户=计数器
.throttle(适用于:0.5,计划程序:DispatchQueue.global(qos:.background),最新版本:true)//放入后台
.receive(on:DispatchQueue.main)//仅限最新结果
.sink{[weak self](值)位于//on@pawello2222注释中
自我?.i=价值
}
DispatchQueue.global(qos:.background).async{
self.startoop()
}
}
}

您为什么要经常更新UI?它将是什么样的数据?我在频繁的UI更新中遇到了类似的问题,关键是使用子视图,而不是所有时候都刷新完整视图。也许这对你也有帮助。没有延迟的无止境循环就是一个例子。传感器(IMU)正在以60 Hz的频率更新,UI的更新速度可能较慢。建议对类使用
sink(receiveValue:)
,以避免保留周期。看,我已经很快地发了你的代码,我自己也能用。我现在要了解combine类到底做了什么。建议对类使用
sink(receiveValue:)
,以避免保留周期。看见