Reputation: 17685
I have a model which is an ObservableObject
. It has a Bool
property, I would like to use this Bool
property to initialise a @Binding
variable.
@ObservableObject
to a @Binding
?@State
the only way to initialise a @Binding
?@ObservedObject
/ @EnvironmentObject
, and I see it's usefulness, but I am not sure a simple button needs to have access to the entire model. 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)
Upvotes: 48
Views: 30907
Reputation: 257493
Binding
variables can be created in the following ways:
@State
variable's projected value provides a Binding<Value>
@ObservedObject
variable's projected value provides a wrapper from which you can get the Binding<Subject>
for all of it's properties@EnvironmentObject
as well.let button = SaleButton(isOn: .init(get: { car.isReadyForSale },
set: { car.isReadyForSale = $0} ))
Note:
@State
/ @ObservedObject
/ @EnvironmentObject
/ @StateObject
(added in SwiftUI 2.0) in the view for SwiftUI to detect changes automatically.$
prefix.Upvotes: 57
Reputation: 197
In my case i used .constant(viewModel)
to pass viewModel to ListView @Binding var viewModel
Example
struct CoursesView: View {
@StateObject var viewModel = CoursesViewModel()
var body: some View {
ZStack {
ListView(viewModel: .constant(viewModel))
ProgressView().opacity(viewModel.isShowing)
}
}
}
struct ListView: View {
@Binding var viewModel: CoursesViewModel
var body: some View {
List {
ForEach(viewModel.courses, id: \.id) { course in
Text(couse.imageUrl)
}
}
}
}
Upvotes: 0
Reputation: 7585
You have several options to observe the ObservableObject
. If you want to be in sync with the state of the object, it's inevitable to observe the state of the stateful object. From the options, the most commons are:
@State
@ObservedObject
@EnvironmentObject
It is upto you, which one suits your use case.
In reality, you will have something like this:
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")
}
}
}
If you are ready for the @EnvironmentObject
you will initialize your view with:
let contentView = ContentView().environmentObject(Car())
Upvotes: 18
Reputation: 27214
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")
}
}
}
Ensure you have the following in your SceneDelegate
:
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
.environmentObject(Car())
Upvotes: 4