【问题标题】:Is this the right way for using @ObservedObject and @EnvironmentObject?这是使用@ObservedObject 和@EnvironmentObject 的正确方法吗?
【发布时间】:2022-10-23 16:39:24
【问题描述】:

牛你给我一些关于我对@ObservedObject 和@EnvironmentObject 的理解的确认?

在我看来,当我们在排序的视图之间“排队”发送数据时,使用 @ObservedObject 很有用,就像 UIKit 中的“准备”一样,而使用 @EnvironmentObject 更像是 UIKit 中的“单例”。我的问题是,我的代码是否正确使用了这两个特性?这是在实际开发中应用的方式吗?

我的模型用作功能的大脑(IE urls 会话,其他数据操作)

class ModelClass_ViaObservedObject: ObservableObject {
    
    @Published var isOn: Bool = true

}

 class ModelClass_ViaEnvironment: ObservableObject {
    
    @Published var message: String = "default"
    
}

我的主要观点

struct ContentView: View {
    //way to send data in views step by step
    @StateObject var modelClass_ViaObservedObject = ModelClass_ViaObservedObject()
    
    //way to share data more or less like a singleton
    @StateObject var modelClass_ViaEnvironment = ModelClass_ViaEnvironment() 
    

    var myBackgroundColorView: Color {
        if modelClass_ViaObservedObject.isOn {
            return Color.green
        } else {
            return Color.red
            
        }
    }


    var body: some View {
        
        NavigationView {
            ZStack {
                myBackgroundColorView
                VStack {
                    NavigationLink(destination:
                                    SecondView(modelClass_viaObservedObject: modelClass_ViaObservedObject)
                    ) {
                        Text("Go to secondary view")
                            .padding()
                            .overlay(
                                RoundedRectangle(cornerRadius: 16)
                                    .stroke(.black, lineWidth: 1)
                            )
                    }
                    
                    Text("text received from second view: \(modelClass_ViaEnvironment.message)")
                    
                }
            }
            .navigationTitle("Titolo")
            .navigationBarTitleDisplayMode(.inline)
            
        }
        .environmentObject(modelClass_ViaEnvironment)
        
    }
    
}

我的第二个观点

struct SecondView: View {
    
    @Environment(\.dismiss) var dismiss
    
    @ObservedObject var modelClass_viaObservedObject: ModelClass_ViaObservedObject
    
    //global data in environment, not sent step by step view by view
    @EnvironmentObject var modelClass_ViaEnvironment: ModelClass_ViaEnvironment


    
    var body: some View {
        
        VStack(spacing: 5) {
            Text("Second View")
            
            Button("change bool for everyone") {
                modelClass_viaObservedObject.isOn.toggle()
                dismiss()
            }
            
            TextField("send back", text: $modelClass_ViaEnvironment.message)
            Text(modelClass_ViaEnvironment.message)

        }
        
    }
}

【问题讨论】:

  • 代表@ObservedObject@EnvironmentObject 的初始类在技术上是相同的。两者都是单一(吨)的事实来源。正如您正确陈述的那样,将其移交给后代视图的方式是不同的。
  • 嗨,瓦迪安,一如既往的犀利!所以你确认我这是处理这个机制的正确方法。谢谢!

标签: swiftui observedobject environmentobject


【解决方案1】:

不,我们使用@State 来查看数据,例如切换isOn,它本身可以是单个值,也可以是包含多个值和变异函数的自定义结构。我们通过在子视图中声明let 将其传递到视图层次结构中,或者如果我们需要写访问权限,则使用@Binding var。无论我们声明它是 let 还是 @Binding,只要将不同的值传递给子 Viewinit,SwiftUI 都会自动调用 body(只要它实际上是在 body 中访问的) .

@StateObject 用于当单个值或自定义结构不起作用并且我们需要引用类型而不是视图数据时,即,如果持久化或同步数据(不使用新的 async/await,因为我们使用 .task那)。对象是init,在调用body 之前(通常在它即将出现之前)和deinit,当不再需要View 时(通常在它消失之后)。

@EnvironmentObject 通常用于在 @Published 属性中保存模型结构并负责保存或同步的存储对象。不同之处在于模型数据不绑定到任何特定的视图,例如 @State@StateObject 用于视图数据。这个对象通常是一个单例,一个用于应用程序,一个用于预览时的示例数据,因为它永远不应该是 deinit。 @EnvironmentObject 相对于@ObservedObject 的优势是我们不需要将它作为let 向下传递给每个视图,当我们只需要它在层次结构的更下方时不需要它。请注意,它必须作为 let 而不是 @ObservedObject 传递的原因是 body 将在中间视图中被不必要地调用,因为 SwiftUI 的依赖跟踪不适用于仅对象值类型。

这是一些示例代码:

struct MyConfig {
    var isOn = false
    var message = ""

    mutating func reset() {
        isOn = false
        message = ""
    }
}

struct MyView: View {
    @State var config = MyConfig() // grouping vars into their struct makes use of value semantics to track changes (a change to any of its properties is detected as a change to the struct itself) and offers testability.

    var body: some View {
        HStack {
            ViewThatOnlyReads(config: config)
            ViewThatWrites(config: $config)
        }
    }
}

struct ViewThatOnlyReads: View {
    let config: MyConfig

    var body: some View {
        Text(config.isOn ? "It's on" : "It's off")
    }
}

struct ViewThatWrites: View {
    @Binding var config: MyConfig
    
    var body: some View {
        Toggle("Is On", isOn: $config.isOn)
    }
}

【讨论】:

  • 谢谢你的解释,首先我用.toggle() 作为例子,但你的观点对我来说很有趣。你能添加一些示例代码吗?我可能会觉得它很有帮助,因为真正的区别似乎仍然是我们需要在每个视图中或仅在其中一些视图中使用相同的引用,并且我可以同时使用 Environment 或 @StateObject 进行逻辑或 url 调用。从你所说的看来我应该改变@StateObject 的使用,为这些简单的功能实现@Binding 。您能否更好地解释您评论的最后 3 行?
  • 最后 3 行:如果您有一个带有 let object 的视图,则当对象发送其 objectWillChange 时不会调用 body(或其 @Published 属性为您执行此操作)。如果您有 @ObservedObject var object,则调用 body。无论object.someProperty 是否在body 中被访问,它都会被调用。这与@State var someState 不同,其中body 仅在someState 在正文中被访问时才被调用。希望清除它。
  • 添加了一些示例代码
猜你喜欢
  • 2013-04-25
  • 2012-05-11
  • 2018-12-31
  • 2021-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多