skip to Main Content

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! 😛

3

Answers


  1. Chosen as BEST ANSWER

    Alright, everything is working nicely. Thanks to all of you guys and especially to @NicolasElPapu.

    Also, the solution is working with the simulator but not with the PreviewProviders. If you still have an error like I had, just update these pieces of code at the bottom.

    struct TopView_Previews: PreviewProvider {
        static var previews: some View {
            TopView(showHello: .constant(true))
        }
    }
    
    struct BottomView_Previews: PreviewProvider {
        static var previews: some View {
            BottomView(showHello: true) // Or .constant(true) again if you're not using a let for the BottomView!
        }
    }
    

    Also, if you want to understand why you need to add a fake information into the PreviewProvider, here's the why: https://www.hackingwithswift.com/forums/swiftui/binding-variable-won-t-preview/10974

    Thanks again guys!


  2. 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 {
        let showHello: Bool
    
        var body: some View {
            Text("This is your message:")
            Text("Hello")
                .opacity(showHello ? 1 : 0)
            }
        
    }
    

    I would recommend some other good resources for SwiftUI:

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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search