skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.


  2. 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:

    SomeDeeplyNestedExampleView(viewModel: viewModel)
    

    And then in the second view should be something like this:

    struct SomeDeeplyNestedExampleView: View {
    
        @StateObject viewModel: ViewModel
        ...
    }
    

    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

    Login or Signup to reply.
  3. 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:

    struct RootView: View {
        @State var shouldHideFAB = false
    
        var body: some View {
            ...
            FAB().opacity(shouldHideFAB == true ? 0 : 1)
            ...
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search