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 View
s 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 View
s 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: