beazer
beazer

Reputation: 31

Pass an @State variable to ContentView

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

Answers (1)

Nikolai
Nikolai

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

Related Questions