Home > Software engineering >  Sharing a @State var Bool between 2 child Views in SwiftUI
Sharing a @State var Bool between 2 child Views in SwiftUI

Time:03-23

I'm a designer who's trying to learn SwiftUI for fun and also to make sure I know a bit more what my developer's team needs from me.

And there's something very simple I just can't do!

As you can see from this piece of code below, I just want to show a text when the var showHello is true, and it's working nicely.

import SwiftUI

struct ContentView: View {

    @State var showHello = true

    var body: some View {

        VStack {
            Spacer()
            Button("Show Hello", action: { showHello.toggle() })
                .padding()
            Spacer()
            Text("This is your message:")
                .padding()
            if showHello == true {
                Text("Hello")
            } else {
                Text("Hello")
                    .opacity(0)
            }
            Spacer()
        }

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

But when I want to do the exact same thing by using multiple views, it doesn't work!

// Parent file

import SwiftUI

struct ContentView: View {

    var body: some View {

        VStack {
            TopView()
            BottomView()
        }

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
// Child file 1

import SwiftUI

struct TopView: View {

    @State var showHello = true

    var body: some View {

        Button("Show Hello", action: { showHello.toggle() })
            .padding()

    }

}

struct TopView_Previews: PreviewProvider {
    static var previews: some View {
        TopView()
    }
}
// Child file 2

import SwiftUI

struct BottomView: View {

    var body: some View {

        Text("This is your message:")
        if showHello == true {
            Text("Hello")
        } else {
            Text("Hello")
                .opacity(0)
        }

    }

}

struct BottomView_Previews: PreviewProvider {
    static var previews: some View {
        BottomView()
    }
}

And I know that we can't share a var from different views so I have spent hours on Internet to learn more about using @State/@Binding or @EnvironmentObject but all the examples are about sharing a var from a parent view to a child view but not between child views into a parent view.

Also, no matter what I'm doing, there's always an error from Xcode because something is missing somewhere!

I need your help, thanks in advance! :P

CodePudding user response:

You don't need to use @Binding in this particular example since you are not changing the state of showHello in the child view. @Binding is for two-way data flow (i.e., from the parent to the child and vice versa). If your data flow is unidirectional (i.e., from the parent to the child), you can just use a simple let in the child view.

Also, instead of this:

if showHello == true {
    Text("Hello")
} else {
    Text("Hello")
        .opacity(0)
}

You should do this:

Text("Hello")
    .opacity(showHello ? 1 : 0)

The first way creates two different and separate Views whereas the second way creates a single View whose opacity modifier you change as needed. Not such a big deal with a simple View hierarchy like this, but once you start getting more complicated hierarchies it will help SwiftUI's view diffing and re-rendering. And it helps with animations since it's a property on one View that is changing rather than two Views switching out.

It's also just easier to read that way.

CodePudding user response:

I have spent hours on Internet to learn more about using @State/@Binding or @EnvironmentObject

So you are on the right path because you do need to use @Binding. Maybe you just had the wrong idea of how to connect the dots.

Basically you need the @State property wrapper in your parent or ContentView and since you want to share that same property in your child views and react to any mutation on it, you use the @Binding property wrapper in the child views.

So you would have something like this:

struct ContentView: View {
    @State var showHello = true

    var body: some View {
        VStack {
            TopView(showHello: $showHello)
            BottomView(showHello: $showHello)
        }
    }
}

struct TopView: View {
    @Binding var showHello: Bool

    var body: some View {
        Button("Show Hello", action: { showHello.toggle() })
            .padding()
    }
}

struct BottomView: View {
    @Binding var showHello: Bool

    var body: some View {
        Text("This is your message:")
        if showHello == true {
            Text("Hello")
        } else {
            Text("Hello")
                .opacity(0)
        }
    }
}

I would recommend some other good resources for SwiftUI:

  • Related