为什么在跨视图使用共享 ObservableObject 时,带有 @StateObject 的 SwiftUI 视图会意外更新?

问题描述 投票:0回答:1

我正在 SwiftUI 中开发一个 iOS 应用程序,其中我有一个共享的 ObservableObject,我想在多个视图之间传递它。该对象旨在跟踪某些共享应用程序状态,例如用户设置或会话数据。

在我的一种观点中,我使用@StateObject来创建该对象的实例。但是,我注意到我对另一个视图中的 ObservableObject 所做的任何更改都会导致我的原始视图意外更新。就好像该对象在多个地方被观察一样,尽管我认为 @StateObject 会使其在声明它的视图中保持隔离。

这是我的代码的简化版本:

import SwiftUI

class SharedData: ObservableObject {
    @Published var counter: Int = 0
}

struct ParentView: View {
    var body: some View {
        VStack {
            CounterView()
            AnotherView()
        }
    }
}

struct CounterView: View {
    @StateObject private var data = SharedData()
    
    var body: some View {
        VStack {
            Text("Counter: \(data.counter)")
            Button("Increment Counter") {
                data.counter += 1
            }
        }
    }
}

struct AnotherView: View {
    @ObservedObject var data = SharedData()  // <- Issue?
    
    var body: some View {
        Text("Another View Counter: \(data.counter)")
    }
}

当我增加 CounterView 中的计数器时,AnotherView 也会反映 data.counter 中的变化。我不明白为什么 AnotherView 会在 CounterView 递增 data.counter 时更新,因为我认为每个视图都会管理自己的实例。

ios swift swiftui state observableobject
1个回答
0
投票

ObservedObject
属性包装器提供的属性不应具有默认值或初始值。

Apple对此也说得很清楚在文档中

不要为观察对象指定默认值或初始值。仅将该属性用于充当视图输入的属性,如上面的示例所示。

因此,

ObservedObject
包装的属性应始终作为依赖项注入到视图中,而永远不要在视图本身中创建。

这样做的背景是,否则,每次重新创建视图时,都会生成一个新的实例

ObservableObject
创建了符合要求的模型实例。如果您想在视图/视图实例之间共享状态,这正是您不想要的。

因此您可以在代码中执行以下操作:

struct ParentView: View {
    @StateObject private var data = SharedData()

    var body: some View {
        VStack {
            CounterView(data: data)
            AnotherView(data: data)
        }
    }
}

struct CounterView: View {
    @ObservedObject var data: SharedData
    
    var body: some View {
        VStack {
            Text("Counter: \(data.counter)")
            Button("Increment Counter") {
                data.counter += 1
            }
        }
    }
}

struct AnotherView: View {
    @ObservedObject var data: SharedData
    
    var body: some View {
        Text("Another View Counter: \(data.counter)")
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.