WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Day 21 - A few questions

Forums > 100 Days of SwiftUI

Good day all!

I have some questions about the 21st day

Here is my code:

struct ContentView: View {
    @State private var showingScore = false
    @State private var ScoreTitle = ""
    @State private var Score = Int()

    @State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Monaco", "Nigeria", "Poland", "Russia", "Spain", "UK", "US"].shuffled()
    @State private var correctAnswer = Int.random(in: 0...2)

    var body: some View {
        ZStack {
            Color.blue
                .ignoresSafeArea()

            VStack(spacing: 30) {
                VStack {
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                    Text(countries[correctAnswer])
                        .foregroundColor(.white)
                }

                ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .renderingMode(.original)
                    }
                }
            }
        }
        .alert(ScoreTitle, isPresented: $showingScore) {
            Button("Continue", action: askQuestion)
        } message: {
            Text("Your score is \(Score)")
        }
    }

    func flagTapped(_ number: Int) {

        if number == correctAnswer {
            ScoreTitle = "Correct"
            Score += 1
        } else {
            ScoreTitle = "Wrong"
        }

        showingScore = true
    }

    func askQuestion() {
        countries.shuffled()
        correctAnswer = Int.random(in: 0...2)
    }

}
  1. I don't quite understand why "number in" was used here after "ForEach (0 .. <3). Could someone explain it to me
ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .renderingMode(.original)
                    }
                }
  1. Why ".alert" modifier is attached to Zstack?

  2. In function flagTapped, "If number == correctAnswer" I don't understand how it works ? How is it related?

    func flagTapped(_ number: Int) {
    
        if number == correctAnswer {
            ScoreTitle = "Correct"
            Score += 1
        } else {
            ScoreTitle = "Wrong"
        }
    
        showingScore = true
    }

   

Maybe this will help

As for the .alert being attached to the ZStack, it could have just as easily been attached to the VStack, or the Button, or the Color. It is one of the modifiers that can be assigned to Views (ZStack, VStack, Color and Button are all defined as View types).

number == correctAnswer

works because the ForEach creates a number of views (for the buttons) and associates each view with the number from number in (actually it creates a tag for the view and links them). When you tap on the image of a flag, it triggers the operation in the closure { … } and passes the number (for that View) to the function flagTapped.

   

One more question :)

  1. What determines the appearance of a good answer in this fragment of the code?
 ForEach(0..<3) { number in
                        Button {
                            flagTapped(number)
                        } label: {
                            Image(countries[number])
                                .renderingMode(.original)
                                .clipShape(Capsule())
                                .shadow(radius: 50)
                        }
                    }

The rest of the code:

func flagTapped(_ number: Int) {

        if number == correctAnswer {
            ScoreTitle = "Correct"
            Score += 1
        } else {
            ScoreTitle = "Wrong, That's flag of \(countries[number])"
        }

        showingScore = true
    }

Because I still don't get it

   

The Button passes the value of number (corresponding the flag tapped because of the linked tag) to the function flagTapped.

In flagTapped

if number == correctAnswer {

is the check for the good answer.

func flagTapped sets the boolean showingScore, which is a State variable, therefore is being watched for changes.

The .alert sees the change of the state of showingScore which triggers it to perform its actions (present the Continue Button and show the current score).

   

I like to think of ForEach as a View factory. You are going to feed some raw bits and pieces into the factory, and the factory is going to create some Views for your application!

So look at this code as the instructions to a factory. And ask yourself, What kind of views is this factory going to make?

// This is a view factory! What kind of views is it going to make?
ForEach(0..<3) { number in
    Button {  //  <----- Here's the answer!  This ForEach factory will make Buttons! A valid type of view....
        flagTapped(number)
    } label: {
        Image(countries[number])
            .renderingMode(.original)
            .clipShape(Capsule())
            .shadow(radius: 50)
    }
}

Next question to ask yourself.... How many buttons is this factory going to make?

ForEach(0..<3) { number in   //  <----- Here's the answer!  This ForEach factory will make three buttons  (0, 1, and 2)
    Button { 
        flagTapped(number)

This ForEach factory is given a range as its parameters. For each integer in this range, the ForEach factory will make ONE button.

In iOS applications, buttons usually have a label and will perform some action when tapped by your user! So ask yourself these final two questions:

  1. What is the label for the button?
  2. What does this button do when it is tapped?
Button {  
        flagTapped(number)  // <-- Here's the answer to question 2. When tapped it runs this function.
    } label: {
        Image(countries[number])  // <-- Here's the answer to question 1. This is the button's label.
            .renderingMode(.original)
            .clipShape(Capsule())
            .shadow(radius: 50)
    }

You can see from the code that the Button's label is a simple Image. Which image? You have many flag images in your application. But your game only uses the first THREE flags in the countries array. Each time you start a new game you tell the array to shuffle itself.

So as the ForEach factory builds a Button, it uses the number you provided ( number in ), and picks the appropriate flag image.

When the player presses a flag button, it executes the flagTapped(number)function. Again, it uses the number (0, 1, or 2) that was provided by the ForEach factory when the button was created.

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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

Reply to this topic…

You need to create an account or log in to reply.

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.