SwiftUI 使用高频数据更新 UI
SwiftUI updating UI with high frequency data
我正在尝试使用来自单独后台线程的高频数据更新主视图。
我创建了两个选项卡视图,如果更新速度较慢,我可以更改视图。但在另一种情况下 UI 没有反应。我只在真实设备上观察到这种行为,在模拟器中一切正常。
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()
}
}
您是否尝试过使用 combine 解决该问题?
如果您的 ui 更新太快,您可以收集一些数据,将其合并到一种缓冲区中,然后在缓冲区已满后,您可以将其推送到您的视图中。另一种方法是针对特定延迟对输入进行去抖动,然后仅将最后一次更新推送到您的视图。例如,假设您有发布的变量 i 不断更新它的值。您可以引入以下视图模型:
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
}
}
}
}
如果您使用 updatedVariable 而不是 i 变量,这样您就可以每 0.5 秒更新一次 ui。
您需要将频率数据的更新存储与表示的 UI 部分分开,因此存储 receives/contains 实际的真实数据,但 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()
}
}
}
我正在尝试使用来自单独后台线程的高频数据更新主视图。 我创建了两个选项卡视图,如果更新速度较慢,我可以更改视图。但在另一种情况下 UI 没有反应。我只在真实设备上观察到这种行为,在模拟器中一切正常。
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()
}
}
您是否尝试过使用 combine 解决该问题? 如果您的 ui 更新太快,您可以收集一些数据,将其合并到一种缓冲区中,然后在缓冲区已满后,您可以将其推送到您的视图中。另一种方法是针对特定延迟对输入进行去抖动,然后仅将最后一次更新推送到您的视图。例如,假设您有发布的变量 i 不断更新它的值。您可以引入以下视图模型:
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
}
}
}
}
如果您使用 updatedVariable 而不是 i 变量,这样您就可以每 0.5 秒更新一次 ui。
您需要将频率数据的更新存储与表示的 UI 部分分开,因此存储 receives/contains 实际的真实数据,但 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()
}
}
}