I have built a primitive quiz app. The ContentView
has a navigationlink
to question one. And in the view of question one (which is a view called questionOne) I have the question and another navigationlink
to questionTwo. It goes on and on until the last question, and in the last question (there are 194 for questions), there is a navigation link to the result view. Because everything is hardcoded like that, the order of the questions are always the same.
What I want to know is, how can I randomize the order of the 194 questions, and after every question is answered, go to the result view at the end.
My limited knowledge suggests that I should create an array of the views and use the array in the navigation link but I do not know how to implement it. And even if I can implement it, I don't know how to not show the same question twice in one sitting and get to the result page at the end.
Here is an examples of my code:
This is the main page:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Capitals of the World Trivia").font(.title)
.fontWeight(.bold)
NavigationLink(destination: questionOne()) {
logoView()
}.buttonStyle(PlainButtonStyle()).padding()
Text("Tap the globe to continue")
}
}
}
}
This is a question view:
var score = 0
// I have a global variable for score,
struct questionOne: View {
@State var counter = 0
@State var isSelected = false
func onClick() {
counter += 1
if counter == 1 { score += 1}
else if counter > 1 { score += 0}
}
var body: some View {
GeometryReader { GeometryProxy in
VStack (alignment: .center) {
Q1Prague().cornerRadius(10)
Spacer()
Text(verbatim: "What's the capital of Czech Republic?")
.font(.title)
.frame(width: 350, height: 80)
Button(action: {self.isSelected.toggle()}) {
Text("Novigrad")
}.buttonStyle(SelectedButtonStyleFalse(isSelected: self.$isSelected))
Spacer()
Button(action: {self.onClick();self.isSelected.toggle()}) {
Text("Prague")
}.buttonStyle(SelectedButtonStyle(isSelected: self.$isSelected))
Spacer()
Button(action: {self.isSelected.toggle()}) {
Text("Ostrava")
} .buttonStyle(SelectedButtonStyleFalse(isSelected: self.$isSelected))
Spacer()
VStack {
HStack {
NavigationLink(destination: questionTwo()) {
Text("Next Question")
}
}
AdView().frame(width: 150, height: 60)
}
}
}.padding(.top, 80).edgesIgnoringSafeArea(.top)
}
}
Here is my last question:
struct question194: View {
@State var counter = 0
@State var isSelected = false
func onClick() {
counter += 1
if counter == 1 { score += 1}
else if counter > 1 { score += 0}
}
var body: some View {
GeometryReader { GeometryProxy in
VStack (alignment: .center) {
Q194Athens().cornerRadius(10)
Spacer()
Text(verbatim: "What's the capital of Greece?")
.font(.title)
.frame(width: 400, height: 80)
Button(action: {self.onClick();self.isSelected.toggle()}) {
Text("Athens")
}.buttonStyle(SelectedButtonStyle(isSelected: self.$isSelected))
Spacer()
Button(action: {self.isSelected.toggle()}) {
Text("Sparta")
}.buttonStyle(SelectedButtonStyleFalse(isSelected: self.$isSelected))
Spacer()
Button(action: {self.isSelected.toggle()}) {
Text("Ostrava")
} .buttonStyle(SelectedButtonStyleFalse(isSelected: self.$isSelected))
Spacer()
VStack {
HStack {
NavigationLink(destination: ResultPage()) {
Text("Next Question")
}
}
AdView().frame(width: 150, height: 60)
}
}
}.padding(.top, 80).edgesIgnoringSafeArea(.top)
}
}
I'm going to give you a couple of how-to-do solutions.
Implementation details will be left for the reader.
You don't want to create a stack of 194 SwiftUI Views (or UIViewControllers). Here's why:
1. Create a test app with just 3 or 4 questions displayed.
2. Run it in the Simulator and go to your last question.
3. Go to Debug | View Debugging | Capture View Hierarchy.
4. Imagine that for 194 question Views.
First solution:
Create an array of objects where each object contains:
a/ Strings, etc. to hold each question and answer and any other question info.
b/ A questionHasBeenShown Bool flag to indicate if the question has been shown.
Create a showQuestionsCounter to keep track of how many questions you have shown.
Filter the array based upon questionHasBeenShown == false.
(You can try removing viewed questions from the original array instead as they are shown. Then you don't need the Bool.)
Use a random number to select the next question.
Increment the counter.
Use the next question you just selected to update all the controls in the View.
You just use one view for this and do not create a new view for each question.
Second solution:
If you insist on having a new View for each question ...
Create a new view for the next question and use the same logic as in the first solution to populate the new view.
Have fun,
David