So basically I have a floating-action-button in the root view of my application so that it can appear on every page. However there are some pages where I want to hide the button. Rather than passing a bunch of bindings down the view tree, I decided to make a simple ViewModel that looks like this:
final class RootViewModel: ObservableObject {
@Published var shouldHideFAB: Bool = false
func hideFAB(value: Bool) {
self.shouldHideFAB = value
}
}
I then implement it like so:
struct RootView: View {
@StateObject viewModel = RootViewModel()
var body: some View {
...
FAB().opacity(viewModel.shouldHideFAB == true ? 0 : 1)
...
}
}
struct SomeDeeplyNestedExampleView: View {
@StateObject viewModel = RootViewModel()
var body: some View {
...
Button {
viewModel.hideFAB(value: true)
} label: {
Text("Hide the button")
}
...
}
}
The problem is when I click the button in the example view, nothing happens in the root view. This works as intended if I pass bindings but that of course is messy however the view model solution I tried doesn’t work. Why is that and how can I fix it?
Thank you!
3
Answers
So as @jnpdx pointed out in the comments, I was creating and using two different instances of the view model. I could use an environmentObject to solve my issue.
This link solved my issue.
So there’s two instances of the view model, as said in the comments. What you need to do is pass your view model into the second view.
For example when you call the second view:
And then in the second view should be something like this:
You should also look into @EnviromentObject a good alternative, which allows you to pass data you need to any view and update them accordingly.
Here’s a good tutorial:
https://hasancanakgunduz.medium.com/swiftui-environmentobject-37fccda94111
In SwiftUI the
View
struct is the view model already, i.e. it’s the data that is used to create/update UIKit views on screen. You should not be creating objects to hold view data you need to use@State
: