提问人:Reinhard Männer 提问时间:9/13/2023 最后编辑:Reinhard Männer 更新时间:9/15/2023 访问量:163
无法停用 SwiftUI View 更新
Cannot disable SwiftUI View updates
问:
我的 App 有两种模式:“
运行”模式执行速度非常快,无需任何 SwiftUI 视图更新,另一种模式是“单步”模式,用户手动推进 App 状态,并在每一步都需要 View 更新。
在单步模式下,SwiftUI 会像往常一样更新我的视图。对于运行模式,我想使用这篇文章中的建议禁用自动视图更新。
为了测试这种方法,我编写了以下最小项目。
如果我确实理解了这个建议,则不应更新,因为相等函数总是返回。
但是视图已更新,我不明白为什么。CustomView
true
编辑:
我的应用程序需要在运行时在运行模式和单步模式之间切换。因此,无法为一种或另一种模式重新生成应用程序。
@Observable
final class Model {
var counter = 0
var timer = Timer()
init() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.counter += 1
}
}
}
@main
struct ViewEquatableApp: App {
let model = Model()
var body: some Scene {
WindowGroup {
CustomView(model: model)
.equatable()
}
}
}
struct CustomView: View, Equatable {
let model: Model
static func == (lhs: CustomView, rhs: CustomView) -> Bool {
// << return yes on view properties which identifies that the
// view is equal and should not be refreshed (ie. `body` is not rebuilt)
true // <- true should disable the View update
}
var body: some View {
ContentView(model: model)
}
}
struct ContentView: View {
let model: Model
var body: some View {
Text("Counter: \(model.counter)")
}
}
答:
您在类中使用了 Apple 的新宏,该宏会在使用此类的任何位置自动处理视图更新。因此,该类中的每个变量都被视为 as 和 in 函数作为内部更新视图,而协议通常会防止外部触发不必要的视图更新(例如由父视图触发)。@Observable
@Published
model
CustomView
@ObservedObject
Equatable
在您的例子中,对类使用常规协议更合适,用 标记属性。然后,您可以在任何需要更新的视图中使用包装器。此外,您不需要实现协议。ObservableObject
@Published
@ObservedObject
Equatable
final class Model: ObservableObject {
@Published var counter = 0
var timer = Timer()
init() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.counter += 1
}
}
}
@main
struct ViewEquatableApp: App {
let model = Model()
var body: some Scene {
WindowGroup {
CustomView(model: model)
}
}
}
struct CustomView: View {
let model: Model
var body: some View {
ContentView(model: model)
}
}
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
Text("Counter: \(model.counter)")
}
}
以这种方式只更新,如果您不希望它更新,请将其删除ContentView
@ObservedObject
评论
@ObservedObject
我下一次尝试禁用 SwiftUI View 更新如下。
我正在学习 SwiftUI,我确信这不是实现它的最佳方式。因此,我们非常欢迎任何关于如何正确操作的建议!
正如 lorem ipsum 在他的上一条评论中指出的那样,问题中概述的方法显然适用于较旧的 SwiftUI 版本,但在使用宏时不再有效。
无论如何,这种方法太复杂了。有一个更简单的解决方案应该始终有效。
当观察到的属性发生更改时,通常会重新绘制 SwiftUI。这通常由 ViewModel 控制,而不是由 DataModel 本身控制。
因此,我更改了代码,以便创建一个传递给:@Observable
App
ViewModel
ContentView
@main
struct MyApp: App {
let viewModel = ViewModel()
var body: some Scene {
WindowGroup {
ContentView(viewModel: viewModel)
}
}
}
ContentView
访问传递的 ,并显示它:viewModelCounter
ViewModel
struct ContentView: View {
let viewModel: ViewModel
var body: some View {
Text("Counter: \(viewModel.viewModelCounter)")
}
}
ViewModel
是。它有一个属性,此处初始化的数据模型,以及一个由 显示的属性。@Observable
model
viewModelCounter
ContentView
Modal
具有一个属性,用于确定是否应将模型中的数据更改转发回 。如果是这样,则更改将允许重新绘制其内容。 还有一个属性,该属性由计时器重复递增,以模拟不断变化的数据。 还引用了它,以便能够在需要时进行更改。mode
ViewModel.viewModelCounter
ViewModel.viewModelCounter
ContentView
Model
modelCounter
Model
ViewModel
viewModel.viewModelCounter
class Model {
var mode = Mode.singleStep
var viewModel: ViewModel?
var modelCounter = 0
var changeDataTimer = Timer()
func changeDataRepeatedly() {
changeDataTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] _ in
self.modelCounter += 1
if mode == .singleStep {
viewModel?.viewModelCounter = modelCounter
}
}
}
}
这个测试应用程序可以按要求工作,但是,如前所述,它肯定不是最好的解决方案。
评论
@Observable
ObservableObject