来自 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.isReadyForSale
与car.$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
的情况下这样做。
它使用环境对象,正如我希望不使用EnvironmentObject
或ObservedObject
的问题中所述,因为我不希望通过一个简单的按钮访问整个模型。跨度>
【参考方案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