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

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

Forums > 100 Days of SwiftUI

@boat  

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

3      

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)
}

4      

@boat  

@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?

3      

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)
}

4      

@boat  

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

3      

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.

4      

@boat  

@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.)

3      

Hacking with Swift is sponsored by Superwall

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

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.