Scenario
一个简单的 SwiftUIApp
由一个TabView
有两个选项卡。这App
结构体有一个@StateObject
属性,该属性正在重复且非常快速地(每秒 30 次)更新simulateFastStateUpdate
.
在这个例子中,simulateFastStateUpdate
没有做任何有用的工作,但它非常类似于快速更新应用程序状态的真实函数。该函数在短时间内在后台队列上执行一些工作,然后在主队列上安排状态更新。例如,当使用相机 API 时,应用程序可能会以每秒 30 次的频率更新预览图像。
Question
当应用程序运行时,TabView
不响应点击。它永久卡在第一个选项卡上。去除liveController.message = "Nice"
线解决了这个问题。
- Why is
TabView
stuck?
- 为什么要更新
@StateObject
导致这个问题?
- 如何调整这个简单的例子,以便
TabView
是不是卡住了?
import SwiftUI
class LiveController: ObservableObject {
@Published var message = "Hello"
}
@main
struct LiveApp: App {
@StateObject var liveController = LiveController()
var body: some Scene {
WindowGroup {
TabView() {
Text(liveController.message)
.tabItem {
Image(systemName: "1.circle")
}
Text("Tab 2")
.tabItem {
Image(systemName: "2.circle")
}
}
.onAppear {
DispatchQueue.global(qos: .userInitiated).async {
simulateFastStateUpdate()
}
}
}
}
func simulateFastStateUpdate() {
DispatchQueue.main.async {
liveController.message = "Nice"
}
// waits 33 ms ~ 30 updates per second
usleep(33 * 1000)
DispatchQueue.global(qos: .userInitiated).async {
simulateFastStateUpdate()
}
}
}
您正在通过这些不断的更新阻塞主线程,并且应用程序正忙于处理您的 UI 更新并且无法处理触摸输入(也在主线程上接收)。
任何创建这种快速事件流的东西都需要被限制。您可以使用组合的throttle
or debounce
减少 UI 更新频率的功能。
看这个示例,我添加了课程UpdateEmittingComponent
生成更新Timer
。这可能是您的后台组件正在快速更新。
In your LiveController
我正在观察组合的结果。我在那里添加了一个throttle
进入管道,这将导致message
发布者通过删除所有中间值每秒触发一次。
删除throttle
最终会出现无响应的情况TabView
.
import SwiftUI
import Combine
/// class simulating a component emitting constant events
class UpdateEmittingComponent: ObservableObject {
@Published var result: String = ""
private var cancellable: AnyCancellable?
init() {
cancellable = Timer
.publish(every: 0.00001, on: .main, in: .default)
.autoconnect()
.sink {
[weak self] _ in
self?.result = "\(Date().timeIntervalSince1970)"
}
}
}
class LiveController: ObservableObject {
@Published var message = "Hello"
@ObservedObject var updateEmitter = UpdateEmittingComponent()
private var cancellable: AnyCancellable?
init() {
updateEmitter
.$result
.throttle(for: .seconds(1),
scheduler: RunLoop.main,
latest: true
)
.assign(to: &$message)
}
}
@main
struct LiveApp: App {
@StateObject var liveController = LiveController()
var body: some Scene {
WindowGroup {
TabView() {
Text(liveController.message)
.tabItem {
Image(systemName: "1.circle")
}
Text("Tab 2")
.tabItem {
Image(systemName: "2.circle")
}
}
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)