Reputation: 31
Building my first SwiftUI app and I'm stuck on pass @State var to the ContentView. I have declared the @State variable in a struct, with an @Binding tag on the variable in the ContentView.
My intent is for multiple instances of NumberBlock to be called in ContentView, and be able to reset all of them to false (hide all of the images) with one button.
The new "App" struct that was added in Xcode 12 is giving an error for a missing parameter. I've tried everything I can think of to enter a parameter, but nothing seems to work. I was able to eliminate the error by using .constant(true), but that did not give me the functionality I need, which is to toggle the variable from the ContentView.
I appreciate any help eliminating the error or correcting my shallow understanding of @State and @Binding.
Here is where I create the @State reset_x var
import SwiftUI
struct NumberBlock: View {
@State var reset_x: Bool = true
@Binding var reset: Bool
var body: some View {
ZStack {
Text("test")
.onTapGesture(count: 1, perform: {
self.reset_x = false
})
Image("XMark")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50, alignment: .center)
.onTapGesture(count: 1, perform: {
self.reset_x = true
print("reset_x is \(self.reset_x)")
})
.isHidden(reset_x ? true : false)
.isHidden(reset ? true : false)
}
}
}
The error occurs in this view:
import SwiftUI
@main
struct Quixx2App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Here is where I want to use the @Binding
import SwiftUI
struct ContentView: View {
@State var reset: Bool = false
@Binding var reset_x: Bool
var body: some View {
VStack {
HStack {
NumberBlock(reset: self.$reset)
NumberBlock(reset: self.$reset)
}
Button("Reset Score"){
self.scoreKeeper.redScore = 0
self.reset_x = false //this line is not doing anything
print("reset_x is \(self.reset_x)")
}
}
}
}
And the .isHidden extension
import Foundation
import SwiftUI
extension View {
@ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View {
if hidden {
if !remove {
self.hidden()
}
} else {
self
}
}
}
Upvotes: 2
Views: 1302
Reputation: 659
So let's start with your NumberBlock
view. You need one state within the View, which preserves if the image is hidden or not. By tapping the Text View, the the value is toggled and the view is rendered accordingly.
struct NumberBlock: View {
@State private var imageIsHidden: Bool
var body: some View {
VStack {
Text("Show")
.onTapGesture(count: 1, perform: {
self.imageIsHidden = false
})
Text("Image")
.onTapGesture(count: 1, perform: {
self.imageIsHidden = true
})
.isHidden(imageIsHidden ? true : false)
}
}
}
Now, you want a second feature: The ContentView
should control this state. So you have to extract this state from the child view (NumberBlock
) to the parent view (ContentView
). By changing the property from @State
to @Binding
you are basically telling the child view that this data is being passed from a parent view.
My working example:
import SwiftUI
import Foundation
extension View {
@ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View {
if hidden {
if !remove {
self.hidden()
}
} else {
self
}
}
}
struct NumberBlock: View {
@Binding var imageIsHidden: Bool
var body: some View {
VStack {
Text("Show")
.onTapGesture(count: 1, perform: {
self.imageIsHidden = false
})
Text("Image")
.onTapGesture(count: 1, perform: {
self.imageIsHidden = true
})
.isHidden(imageIsHidden ? true : false)
}
}
}
struct ContentView: View {
@State var imageOfBlock1IsHidden: Bool = true
@State var imageOfBlock2IsHidden: Bool = true
var body: some View {
VStack {
HStack {
NumberBlock(imageIsHidden: self.$imageOfBlock1IsHidden)
NumberBlock(imageIsHidden: self.$imageOfBlock2IsHidden)
}
Button("Reset Score") {
self.imageOfBlock1IsHidden = true
self.imageOfBlock2IsHidden = true
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 0