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

Day 34 Project 6 Challenges

Forums > 100 Days of SwiftUI

I'm on the first challenge and I manage to get the flag to spin but now the alert that shows whether their answer was correct doesn't show up. I can't figure out what is going on. Any input would be appreciated.

Here is my code:

//
//  ContentView.swift
//  GuessTheFlag
//
//

import SwiftUI

struct FlagImage: View {
    let country: String
    @State private var rotationAngle: Double = 0.0 // Add a state variable to hold the rotation angle

    var body: some View {
        Image(country)
            .renderingMode(.original)
            .clipShape(Capsule())
            .shadow(radius: 5)
            .rotation3DEffect(.degrees(rotationAngle), axis: (x: 0.0, y: 1.0, z: 0.0)) // Apply the rotation effect using the state variable
            //.animation(.easeInOut(duration: 1.0), value: true) // Add an animation modifier to control the rotation speed
            .onTapGesture {
                rotateFlag() // Call the rotateFlag function when the button is tapped
            }
    }

    func rotateFlag() {
        withAnimation {
            rotationAngle += 360.0 // Rotate the flag by 360 degrees
        }
    }
}

/* struct FlagImage: View
{
    let country: String

    var body: some View
    {
        Image(country)
            .renderingMode(.original)
            .clipShape(Capsule())
            .shadow(radius: 5)
            .rotation3DEffect(.degrees(360), axis: /*@START_MENU_TOKEN@*/(x: 0.0, y: 1.0, z: 0.0)/*@END_MENU_TOKEN@*/)
    }
}*/

struct ContentView: View
{
    @State private var showingScore = false
    @State private var scoreTitle = ""

    @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 score = 0
    @State private var lastScore = 0
    @State private var numberOfGamesPlayed = 0

    var body: some View
    {
        ZStack
        {
            RadialGradient(stops: [ .init(color: Color(red: 0.1, green: 0.2, blue: 0.45), location: 0.3),
                                    .init(color: Color(red: 0.76, green: 0.15, blue: 0.26), location: 0.3)
            ], center: .top, startRadius: 200, endRadius: 700)
                .ignoresSafeArea()
            VStack
            {
                Spacer()
                Text("Guess the flag")
                    .font(.largeTitle.bold())
                    .foregroundStyle(.white)

                VStack(spacing: 15)
                {
                    VStack
                    {
                        Text("Tap the flag of")
                            .foregroundStyle(.secondary)
                            .font(.subheadline.weight(.heavy))
                        Text(countries[correctAnswer])
                            .font(.largeTitle.weight(.semibold))
                    }

                    ForEach(0..<3)
                    {
                        number in
                        Button
                        {
                            flagTapped(number)
                        } label: {
                            FlagImage(country: countries[number])
                        }
                    }
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 20)
                .background(.regularMaterial)
                .clipShape(RoundedRectangle(cornerRadius: 20))

                Spacer()
                Spacer()
                Text("Score: \(score)")
                    .foregroundStyle(.white)
                    //.foregroundStyle(.linearGradient(Gradient(stops: [.init(color: .white, location: 0.2), .init(color: .blue, location: 0.5)]), startPoint: .leading, endPoint: .trailing))
                    .font(.title.bold())
                Spacer()
            }
            .padding()
        }
        .alert(scoreTitle, isPresented: $showingScore)
        {
            Button("Continue", action: askQuestion)
            Button("Reset", action: resetGame)
        } message: {

                Text("Your score is \(lastScore)")
            }
    }

    func flagTapped(_ number: Int)
    {
        numberOfGamesPlayed += 1
        print("Numbers of games played \(numberOfGamesPlayed)")

        if number == correctAnswer && numberOfGamesPlayed < 8
        {
            scoreTitle = "Correct"
            score += 1
            lastScore = score
            print("Last score is \(lastScore)")
        }
        else if numberOfGamesPlayed == 8 && number == correctAnswer
        {
            score += 1
            lastScore = score
            calculateJudgementResults()
        }
        else if numberOfGamesPlayed == 8 && number != correctAnswer
        {
            lastScore = score
            calculateJudgementResults()

        }
        else if number != correctAnswer
        {
            scoreTitle = "Wrong! That's the flag of \(countries[number])"
        }

        showingScore = true
    }

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

    func calculateJudgementResults()
    {
        let textToDisplay = "Reset the game. Your final score is \(lastScore)."

        if score >= 4 && score <= 5
        {
            scoreTitle = textToDisplay + " Average."
        }
        else if score >= 6 && score <= 7
        {
            scoreTitle = textToDisplay + " Pretty good."
        }
        else if score == 8
        {
            scoreTitle = textToDisplay + " Excellent."
        }
        else
        {
            scoreTitle = textToDisplay + " Better luck next time."
        }
        resetGame()
    }

    func resetGame()
    {
        lastScore = score
        print("Final last score is \(lastScore)")
        score = 0
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
        numberOfGamesPlayed = 0
    }
}

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

Here is the problem code:

.alert(scoreTitle, isPresented: $showingScore)
        {
            Button("Continue", action: askQuestion)
            Button("Reset", action: resetGame)
        } message: {

                Text("Your score is \(lastScore)")
            }

It worked before I added this code:

struct FlagImage: View {
    let country: String
    @State private var rotationAngle: Double = 0.0 // Add a state variable to hold the rotation angle

    var body: some View {
        Image(country)
            .renderingMode(.original)
            .clipShape(Capsule())
            .shadow(radius: 5)
            .rotation3DEffect(.degrees(rotationAngle), axis: (x: 0.0, y: 1.0, z: 0.0)) // Apply the rotation effect using the state variable
            //.animation(.easeInOut(duration: 1.0), value: true) // Add an animation modifier to control the rotation speed
            .onTapGesture {
                rotateFlag() // Call the rotateFlag function when the button is tapped
            }
    }

    func rotateFlag() {
        withAnimation {
            rotationAngle += 360.0 // Rotate the flag by 360 degrees
        }
    }
}

1      

It looks like flagTapped is not being called when I click on a flag. I can't figure out how to fix this.

1      

Why don't people help in this forum anymore? My last few posts have gone unanswered.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

The .onTapped gesture is what is preventing the flagTapped button from being called. How do I use both?

1      

Hi @morpheus! I would suggest you to look into your button structure.

Button
    {
        flagTapped(number)
    } label: {
        FlagImage(country: countries[number])
    }

If you say that you alert is not triggered but rotation works, I can assume that FlagImage structure that contains .onTapGesutre modifier overrides that tap on the button structure.

The simplest way which is offered by Paul if not mistaken is to declare

@State private var selectedFlag = -1

then in your func flagTapped(_ number: Int) you change it to selectedFlag = number at the very beginning. And then you can make the button rotate and remove that .onTapGesture completely.

ForEach(0..<3) { number in
    Button {
        flagTapped(number)
    } label: {
        FlagImage(name: countries[number])
            .rotation3DEffect(.degrees(selectedFlag == number ? 360 : 0), axis: (x: 0, y: 1, z: 0))
            .animation(.default, value: selectedFlag)
    }
}
struct FlagImage: View {
    var name: String

    var body: some View {
        Image(name)
            .renderingMode(.original)
            .clipShape(RoundedRectangle(cornerRadius: 20))
            .shadow(radius: 5)
    }
}

2      

then in your func flagTapped(_ number: Int) you change it to selectedFlag = number at the very beginning. And then you can make the button rotate and remove that .onTapGesture completely.

Where do I declare selectedFlag? At the start of ContentView? Where do I change it in the flagTapped func?

@State private var selectedFlag = -1

1      

So I changed the ForEach loop to your code:

ForEach(0..<3)
                    {
                        number in
                        Button
                        {
                            flagTapped(number)
                        } label: {
                            FlagImage(country: countries[number])
                                        .rotation3DEffect(.degrees(selectedFlag == number ? 360 : 0), axis: (x: 0, y: 1, z: 0))
                                        .animation(.default, value: selectedFlag)

//                            Image(countries[number])
//                                .renderingMode(.original)
//                                .clipShape(Capsule())
//                                .shadow(radius: 5)
                        }
                    }

I changed the flagTapped func to what I think its suppose to be:

func flagTapped(_ number: Int)
    {
        selectedFlag = number
        numberOfGamesPlayed += 1
        print("Numbers of games played \(numberOfGamesPlayed)")

        if number == correctAnswer && numberOfGamesPlayed < 8
        {
            scoreTitle = "Correct"
            score += 1
            lastScore = score
            print("Last score is \(lastScore)")
        }
        else if numberOfGamesPlayed == 8 && number == correctAnswer
        {
            score += 1
            lastScore = score
            calculateJudgementResults()
        }
        else if numberOfGamesPlayed == 8 && number != correctAnswer
        {
            lastScore = score
            calculateJudgementResults()

        }
        else if number != correctAnswer
        {
            scoreTitle = "Wrong! That's the flag of \(countries[number])"
        }

        showingScore = true
    }

The alerts work all the time now but the rotation is all over the place. Sometimes the selected flag rotates, other times multiple flags rotate. Sometimes it does not rotate at all. And on top of all that the alert covers the middle flag so you cannot see if it rotated or not. I have been stuck on this coding assignment for several days and I am getting behind. I really need help figuring this out. @ygeras has the easiest solution to implement but its just not working as expected.

1      

I think I am thinking about this wrong. The flag should spin when you click it whether its right or wrong. If that is the case I think the code works.

1      

I added a few additions to my alert to reset selectedFlag back to -1 after it spins and it appears everything is working right.

1      

The flag should spin when you click it whether its right or wrong. If that is the case I think the code works.

yes this is exactly the idea behind it.

And on top of all that the alert covers the middle flag so you cannot see if it rotated or not.

to solve this issue you can make alert to show up with delay in func flagTapped. Something like that should do the trick.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  showingScore = true
}

1      

to solve this issue you can make alert to show up with delay in func flagTapped. Something like that should do the trick.

Delayed alert:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  showingScore = true
}

0.5 was too fast but 1 was just right.

1      

Good point @Obelix but your code changed too much and was too hard to follow along. @ygeras code fell in line with my existing code and was easier to follow along.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.