来自 ObservableObject 的绑定值

Posted

技术标签:

【中文标题】来自 ObservableObject 的绑定值【英文标题】:Binding value from an ObservableObject 【发布时间】:2020-04-03 05:00:05 【问题描述】:

目标:

我有一个模型是 ObservableObject。它有一个Bool 属性,我想用这个Bool 属性来初始化一个@Binding 变量。

问题:

    如何将@ObservableObject 转换为@Binding? 创建@State 是初始化@Binding 的唯一方法吗?

注意:

我知道我可以使用@ObservedObject / @EnvironmentObject,我看到它很有用,但我不确定一个简单的按钮是否需要访问整个模型。 还是我的理解有误?

代码:

import SwiftUI
import Combine
import SwiftUI
import PlaygroundSupport

class Car : ObservableObject 

    @Published var isReadyForSale = true


struct SaleButton : View 

    @Binding var isOn : Bool

    var body: some View 

        Button(action: 

            self.isOn.toggle()
        ) 
            Text(isOn ? "On" : "Off")
        
    


let car = Car()

//How to convert an ObservableObject to a Binding
//Is creating an ObservedObject or EnvironmentObject the only way to handle a Observable Object ?

let button = SaleButton(isOn: car.isReadyForSale) //Throws a compilation error and rightly so, but how to pass it as a Binding variable ?

PlaygroundPage.current.setLiveView(button)

【问题讨论】:

【参考方案1】:

Binding变量可以通过以下方式创建:

    @State 变量的预计值提供了 Binding<Value> @ObservedObject 变量的投影值提供了一个包装器,您可以从中获取 Binding<Subject> 的所有属性 第 2 点也适用于 @EnvironmentObject。 您可以通过为 getter 和 setter 传递闭包来创建 Binding 变量,如下所示:
let button = SaleButton(isOn: .init(get:  car.isReadyForSale ,
                                    set:  car.isReadyForSale = $0 ))

注意:

正如@nayem 所指出的,您需要在视图中使用@State / @ObservedObject / @EnvironmentObject / @StateObject(在SwiftUI 2.0 中添加),以便SwiftUI 自动检测更改。 使用$前缀可以方便地访问投影值。

【讨论】:

我通过@Asperi 找到了几个与 SwiftUI 相关的有用/需要的答案 - 继续贡献:thumbs-up: 方式二,直接通过$car.isReadyForSale获取Binding <Value> 很容易将$car.isReadyForSalecar.$isReadyForSale 混合在一起(就像我所做的那样)。后者会产生一个Published<Value>.Publisher,这不是我们想要的。 (其实@yuanjilee,这是最好的答案) 这就像你用 ReactJS 做的一样。您向子视图传递一个函数,该函数会在其定义的上下文中更新状态。【参考方案2】:
struct ContentView: View 
    @EnvironmentObject var car: Car

    var body: some View 
        SaleButton(isOn: self.$car.isReadyForSale)
    


class Car: ObservableObject 
    @Published var isReadyForSale = true


struct SaleButton: View 
    @Binding var isOn: Bool

    var body: some View 
        Button(action: 
            self.isOn.toggle()
        ) 
            Text(isOn ? "On" : "Off")
        
    

确保您的SceneDelegate 中有以下内容:

// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
    .environmentObject(Car())

【讨论】:

谢谢,我完全同意我们可以通过 State 来做,想知道是否有办法不使用 State(如问题中所述) @user1046037 在没有State 的情况下这样做。 它使用环境对象,正如我希望不使用EnvironmentObjectObservedObject 的问题中所述,因为我不希望通过一个简单的按钮访问整个模型。跨度> 【参考方案3】:

    您有几个选项来观察ObservableObject。如果要与对象的状态保持同步,则不可避免地要观察 stateful 对象的状态。从选项中,最常见的是:

    @State @ObservedObject @EnvironmentObject

这取决于您,哪一个适合您的用例。

    没有。但是您需要有一个对象,该对象可以在任何时间点观察到对该对象所做的任何更改。

实际上,你会有这样的东西:

class Car: ObservableObject 
    @Published var isReadyForSale = true


struct ContentView: View 

    // It's upto you whether you want to have other type 
    // such as @State or @ObservedObject
    @EnvironmentObject var car: Car

    var body: some View 
        SaleButton(isOn: $car.isReadyForSale)
    



struct SaleButton: View 
    @Binding var isOn: Bool
    var body: some View 
        Button(action: 
            self.isOn.toggle()
        ) 
            Text(isOn ? "Off" : "On")
        
    

如果您准备好使用@EnvironmentObject,您将使用以下命令初始化您的视图:

let contentView = ContentView().environmentObject(Car())

【讨论】:

谢谢,希望该按钮无法访问整个 Car 模型。我找不到创建绑定的方法。正如 Asperi 指出的那样,Binding 有一个初始化器,它接收一个 getter 和 setter 好吧@user1046037,我明白了。实际上,除非您故意将其注入 Button 对象,否则您的按钮将无法访问 Car 模型。如果您考虑在按钮的容器/父级中使用@EnvironmentObject,并且您没有在SaleButton 中定义指向@EnvironmentObject 的访问点,则该对象根本不会暴露给按钮对象。除此之外,如果您在容器中使用@State@ObservedObject,您将无法从SaleButton 引用该对象,而无需故意传递该对象。 但我担心你指出的@Asperi 提供的解决方案是否能应付你的用例。因为很明显,即使您的按钮可以改变汽车对象的状态,您的按钮也无法刷新其视图,因为您没有帮助 SwiftUI 记住汽车实例的状态。 这是正确的,但是它打开了不将整个模型传递给按钮的可能性,而只是传递一个会更新的闭包。我同意必须使用@ObservedObject,但是我从来不知道有一种方法可以弥合从 Observable Object 到 Binding 的差距(通过从 get / set 闭包创建绑定)。您的解决方案无需通过整个模型即可运行 是的!你也可以自己试试。只需将您的 Car 设为结构即可。出于测试目的,您可以在结构对象的属性中添加属性观察者didSet

以上是关于来自 ObservableObject 的绑定值的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 双向绑定到枚举案例中 ObservableObject 中的值

如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject

如果数组是 ObservableObject 的成员,如何绑定数组和 List?

从一个属性中提取价值到另一个属性中

html 来自http://wiki.jikexueyuan.com/project/kendo-ui-development-tutorial/Kendo-mvvm-observableobject

如何使用 ObservableObject 更新 UIViewRepresentable