UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Day 35 Challenge: Problem using @State variables across multiple Views

Forums > 100 Days of SwiftUI

I am working on the day 35 challenge of 100 days of SwiftUI but I don't know how to proceed. The instructions say "Try and break up your layouts into new SwiftUI views, rather than putting everything in ContentView." But when I try to create separate Views for "Setting Selection", "Gameplay", and "Game Over", I run into problems with accessing the @State private vars that I created in Content View.

For example, in ContentView I am trying to use "@State private var settingsSelected: Bool" to determine whether the "SelectSettingsView" or the "GameplayView" should be displayed. However, I don't know how to give ContentView access to read the settingsSelected variable, while also giving SelectSettingsView a button that would be able to toggle settingsSelected to True so ContentView could read it.

I may have missed something in the previous lessons, but it doesn't seem to me that this has been covered in the previous course material yet.

This is the code that I have so far, and it is in the definition of "struct SettingSelectionView: View" that I am getting errors.

import SwiftUI

struct ContentView: View {
    @State private var numQuestionsIndex = 0
    @State private var settingsSelected = false
    @State private var gameOver = false
    @State private var difficulty = 0
    @State private var questions = [Question]()
    @State private var currentQuestion = 0

    let numQuestionsOptions = ["5", "10", "20", "All"]
    let maxDifficulty = 12
    let indexOfAllQuestions = 3

    var numQuestions: Int {
        questions.count
    }

    var correctAnswer: Int {
        questions[currentQuestion].answer
    }

    func generateQuestions() {
        if numQuestionsIndex < indexOfAllQuestions {
            //player chose a scpecific number of questions to answer
            for _ in (0..<Int(numQuestionsOptions[numQuestionsIndex])!) {
                let firstFactor = Int.random(in: 0...difficulty)
                let secondFactor = Int.random(in: 0...difficulty)
                questions.append(Question(firstFactor: firstFactor, secondFactor: secondFactor))
            }
        } else {
            //player chose to answer all possible questions of the selected difficulty
            for factor in (0...difficulty) {
                var secondFactor = 0
                while secondFactor <= difficulty {
                    questions.append(Question(firstFactor: factor, secondFactor: secondFactor))
                    secondFactor+=1
                }
            }
        }
    }

    var body: some View {
        Group {
            if !settingsSelected {
                SettingSelectionView()
            } else if !gameOver {
                GameplayView()
            } else {
                GameOverView()
            }
        }
    }
}

struct Question {
    var question: String
    var answer: Int

    init (firstFactor: Int, secondFactor: Int) {
        question = "\(firstFactor) x \(secondFactor) = ?"
        answer = firstFactor * secondFactor
    }
}

struct SettingSelectionView: View {
    var body: some View {
        NavigationView{
            VStack {
                Form {
                    Section(header: Text("Select Difficulty:")) {
                        Stepper(value: $difficulty, in: 1...maxDifficulty, step: 1) {
                            Text("\(difficulty)")
                        }
                    }
                    .font(.headline)

                    Section(header: Text("Number of Questions to Practice:")) {
                        Picker("", selection: $numQuestionsIndex) {
                            ForEach(0 ..< self.numQuestionsOptions.count) {number in
                                Text(self.numQuestionsOptions[number])
                            }
                        }
                        .pickerStyle(SegmentedPickerStyle())
                    }
                    .font(.headline)
                }

                Button(action: {
                    self.settingsSelected.toggle()
                }) {
                    Text("Start")
                }
                .padding(20)
            }
            .navigationBarTitle(Text("Select Settings"))
        }
    }
}

struct GameplayView: View {
    var body: some View {
        Text("Play Game")
    }
}

struct GameOverView: View {
    var body: some View {
        Text("Game Over")
    }
}

3      

You would pass the parameters into the SettingSelectionView using @Binding, as you want to modify the values in the parameters using the Stepper and Picker functions.

Also the constants maxDifficulty and numQuestionsOptions need to be passed from your main ContentView to the SettingSelectionView struct.

struct SettingSelectionView: View {
    @Binding var difficulty: Int
    @Binding var numQuestionsIndex: Int
    @Binding var settingsSelected: Bool

    let maxDifficulty: Int
    let numQuestionsOptions: [String]

    var body: some View {
        NavigationView{
            VStack {

            // remainder of the code

The in the main ContentView

var body: some View {
    Group {
        if !settingsSelected {
            SettingSelectionView(difficulty: $difficulty,
                                 numQuestionsIndex: $numQuestionsIndex,
                                 settingsSelected: $settingsSelected,
                                 maxDifficulty: maxDifficulty,
                                 numQuestionsOptions: numQuestionsOptions)

        } else if !gameOver {
            GameplayView()
        } else {
            GameOverView()
        }
    }
}

4      

I'll try it tomorrow, but it looks like that will probably work for me. I just looked up @Binding on this site though, and found that it isn't introduced until project 11. I'm only between projects 6 and 7 right now, so there must have been some other way that the instructor intended this challenge to be done. But, this solution should be good enough to work for me. Thank you for your reply.

3      

An alternative would be to use a function rather than a structure. The function would return 'some View' and reside inside the main ContentView structure. That doesn't involve @Binding.

As there is only one item in the function, you could omit the return statement. For my own functions, even if there is only one statement in the closure, I prefer to leave the return in as a matter of habit, just in case later I go in and do more in the function. It is just clearer for me.

func settingSelectionView() -> some View {
    return
        VStack {
            Form {
                Section(header: Text("Select Difficulty:")) {
                    Stepper(value: $difficulty, in: 1...maxDifficulty, step: 1) {
                        Text("\(difficulty)")
                    }
                }
                .font(.headline)

                Section(header: Text("Number of Questions to Practice:")) {
                    Picker("", selection: $numQuestionsIndex) {
                        ForEach(0 ..< self.numQuestionsOptions.count) {number in
                            Text(self.numQuestionsOptions[number])
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                .font(.headline)
            }

            Button(action: {
                self.settingsSelected.toggle()
            }) {
                Text("Start")
            }
            .padding(20)
        }
}

var body: some View {
    Group {
        if !settingsSelected {
            settingSelectionView()

        } else if !gameOver {
            GameplayView()

        } else {
            GameOverView()
        }
    }
}

4      

Thank you, I tried using struct and binding but its not working, so I use function instead.

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.