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

SOLVED: Question: Project 2 ForEach( number ) and flagTapped(number )

Forums > 100 Days of SwiftUI

I'm working on Project 2

https://www.hackingwithswift.com/books/ios-swiftui/showing-the-players-score-with-an-alert

This is what Paul wrote:


import SwiftUI

struct ContentView: View {

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

    @State private var showingScore = false
    @State private var scoreTitle = ""

    var body: some View {

        VStack {
            VStack{
                Text ("Tap the flag of")
                Text (countries[correctAnswer])
            }
            VStack{
                ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .renderingMode(.original)
                    }
                }
            }
        }
    }
    func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
        } else {
            scoreTitle = "Wrong"
        }

        showingScore = true
    }

}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

My Questions is:

Why func flagTapped is integrated in the ForEach looping ?

ForEach is there for displaying three flag images, right ? In the sequence of events, shouldn't it be:

three image displayed through ForEach -> user choose the answer through flagTapped, right ?

Hence, the number of the images shown, and the nubmer of user choosed , shoudn't be the same number parameters, rights ?

However, the syntx make it looks like it is the same number used in this ForEach paramters (0..<3) , as they are all in the ForEach scope.

 ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .renderingMode(.original)

So how could Swift know which number flagTapped used ?

Thank you in advance ,

Boat

1      

What maybe confusing here is that the RandomAccessCollection (of elements) required by the ForEach is the range of integers 0..<3, and number is the element (in this case integer) in any one particular interation of the ForEach loop. So number is different each time round the loop.

If you select the keyword ForEach and option-click on it to show the definition of it.

Possibly the easiest way to think of the way ForEach works is think of it like this:-

Quite literally, for each of the collection of things I am going to give you, give me one of them at a time and I am going to do something with it.

// assuming collectionOfThings and doSomethingWith are suitably defined elsewhere

ForEach(collectionOfThings) { oneOfThemAtATime in
    doSomethingWith(oneOfThemAtATime)
}

2      

@Greenamberred, thanks,

but still, in the code Paul wrote

 ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])

ForEach has to loop 0 ,1, 2 to show three images, right ?
However, according to the above code, when it run "0" to show image #1, Button{} has been run ;
when it run "1" to show image #2 , Button{} has been run again; when it run "2", Button { } runs again,

But supposedly, Button should only run once , and that is after when all three Images have been all shown and when the user made his/her choice.

NO?

1      

Nothing is "run" when the ForEach is looped through. The ForEach builds the View(s) inside it however many times it loops.

So, in the case of ForEach(0..<3), three Buttons are created, with number referring to each iteration through the loop.

What results is essentially this:

Button {
    flagTapped(0)
} label: {
    Image(countries[0])
        .renderingMode(.original)
} 
Button {
    flagTapped(1)
} label: {
    Image(countries[1])
        .renderingMode(.original)
}
Button {
    flagTapped(2)
} label: {
    Image(countries[2])
        .renderingMode(.original)
}

2      

Hello Now I got the idea that ForEach is there for making views.

Like @Obelix said, Button is "fancy listing", meaning, rather than modestly showing images, these images actually bearing functions. Otherwise, why choose Button rather than Image, right ?

Therefore, with Button bearing flagTapped function, if there's no flag tapped, the function aspect of Button won't be triggerd, and these buttons will be used merely for showing its lables (flag images in this case), which is good enough for ForEach to make views already. Right ?

So back to my original question, the (number) in flagTapped(number) will only be applicable when that specific Button is tapped. Right ?

Thanks again everyone,

Boat

1      

I think that Obelix's 'fancy listing' was just because it was an upmarket restaurant ;-)

You're right that buttons do more than images - they have two parts - how they look and what they do when you tap on them.

A Button conforms (meaning matches and follows the template) for the View protocol, which is what the ForEach requires and expects. So yes, you are right.

In essence you are right, the flagTapped(number) action is only triggered for the corresponding Button (flag image) tapped with the appropriate number value.

Maybe reading Paul's tutorials on buttons might help.

2      

@Greenamberred , probably @Obelix 's facny restaurant is just a fake cake kitchen serving nothing, like he confessed in my thread asking about 'returning' ! LOL

Yes I just read the Button piece you provided above, very helpful.

Thank you @Greenamberred @roosterboy @Obelix for putting up with all my silly questions. I wil keep it coming. I struggle a bit when it becomes abstract. But I keep trying because I strongly believe in my biz idea where I will only need a simple app to get it off the ground.

Love from Singapore,

Boat

(becuse of the time difference, I couldn't attend Paul's livestream yesterday, but my love for this community is growing everyday.)

1      

Hacking with Swift is sponsored by Fernando Olivares

SPONSORED Fernando's book will guide you in fixing bugs in three real, open-source, downloadable apps from the App Store. Learn applied programming fundamentals by refactoring real code from published apps. Hacking with Swift readers get a $10 discount!

Read the book

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.