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

目标:

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

问题:

  1. 如何将@ObservableObject 转换为@Binding
  2. 创建@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)

【问题讨论】:

    标签: swift swiftui combine


    【解决方案1】:

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

    1. @State 变量的预计值提供了 Binding<Value>
    2. @ObservedObject 变量的投影值提供了一个包装器,您可以从中获取 Binding<Subject> 的所有属性
    3. 第 2 点也适用于 @EnvironmentObject
    4. 您可以通过为 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】:
    1. 您有几个选项来观察ObservableObject。如果要与对象的状态保持同步,则不可避免地要观察 stateful 对象的状态。从选项中,最常见的是:

      • @State
      • @ObservedObject
      • @EnvironmentObject

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

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

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

    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
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-20
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多