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

SOLVED: Project 6 part 3 challenge -making the flag rotation animation comes before flags change

Forums > 100 Days of SwiftUI

I tried to add the animation in the first challenge of Project 6, but it always comes after the next question. Is there a way to make the correct flag rotates before flagTapped method run?

3      

Can you share your code, so we could help more specifically

3      

Thank you @Sangsom for your reply, I solved the issue using:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {self.askQuestion()}

and this is my code:

struct ImageModifier: ViewModifier{

    func body(content: Content) -> some View {
        content
        .clipShape(Capsule())
        //.overlay(Capsule().stroke(Color.black, lineWidth: 1))
        //.shadow(color: .black, radius: 2)
    }
}
struct LargeRed: ViewModifier {
    func body(content: Content) -> some View{
       content
        .font(.largeTitle)
        .foregroundColor(.red)
        .frame(width: UIScreen.main.bounds.width)
    }
}

extension View{
    func largeRed() -> some View {
        self.modifier(LargeRed())
        }
    func imageModifier() -> some View {
            self.modifier(ImageModifier())
        }
}
struct ContentView: View {
    @State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Russia", "Spain", "UK", "US"]
    @State private var correctAnswer = Int.random(in: 0...2)
    @State private var showingScore = false
    @State private var score = 0
    @State private var userTapped = 0
    @State private var animationAmount = 0.0
    @State private var opacity = 1.0
    @State private var offset = CGFloat.zero
    @State private var disabled = false

    var body: some View {
        ZStack{
            LinearGradient(gradient: Gradient(colors: [.blue, .black]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)
        VStack(spacing: 30){
            VStack(spacing: 30) {
            Text("Tap the flag of")
                .foregroundColor(.white)
                .padding(.top, 50)
                .font(.largeTitle)
            Text(countries[correctAnswer])
                .largeRed()
            }

            Spacer()

            ForEach(0 ..< 3) { number in
                Button(action: {
                    if number == self.correctAnswer {
                        self.flagTapped(number)
                        self.userTapped = number
                        withAnimation(.easeInOut(duration: 2)) {
                            self.animationAmount += 360
                            self.opacity -= 0.75
                        }
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2)
                        {self.askQuestion()}
                    } else {
                        self.flagTapped(number)
                        self.userTapped = number
                        withAnimation(.easeInOut(duration: 0.5)) {
                            self.offset = 200
                        }
                    }
                    self.disabled = true
                }) {
                    Image(self.countries[number])
                        .renderingMode(.original)
                        .imageModifier()
                }
            .rotation3DEffect(.degrees(self.animationAmount), axis: (x: 0, y: number == self.userTapped ? 1 : 0, z: 0))
                .offset(x: number != self.correctAnswer ? self.offset : .zero, y: .zero)
                .clipped()
                .opacity(number != self.userTapped ? self.opacity : 1.0)
                .disabled(self.disabled)
            }

            Spacer()
            ZStack{

                Color.red
                    .clipShape(Circle())
                    .frame(width: 150, height: 100)
                Text("\(score)")

            }

            .foregroundColor(.white)
            .font(.largeTitle)

            }
        }
        .alert(isPresented: $showingScore) {
            Alert(title: Text("Wrong"), message: Text("This is the flag of \(countries[userTapped])"), dismissButton: .default(Text("Continue")){
                self.askQuestion()
                })
        }
    }

    func flagTapped (_ number: Int){
        if number == correctAnswer{
            score += 1
        } else{
            showingScore = true
        }
    }

    func askQuestion(){
        self.disabled = false
        self.opacity = 1
        self.offset = .zero
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
    }
}

3      

Probably spent more time than I should have on this, but I learned a lot. Here is an implementation I came up with... (revised)

import SwiftUI

enum AnswerStatus {
    case unanswered, correct, incorrect
}

struct ContentView: View {
    let animationTime = 0.25
    let affirmations = ["Way To Go!", "You Got It!", "Oh Yeah!", "Damn Straight!", "Nailed It!", "Boo-yah!"].shuffled()
    let encouragements = ["Darn!", "Try again!", "So close!", "Almost!", "Shoot!", "Next time!"].shuffled()
    @State private var numCorrect = 0
    @State private var numIncorrect = 0
    @State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Russia", "Spain", "UK", "US" ].shuffled()
    @State private var correctAnswerNum = Int.random(in: 0...2)
    @State private var answerStatus = AnswerStatus.unanswered
    @State private var tappedNum = 0
    @State private var message = Array("")
    @State private var animationInterval = 0.0

    var body: some View {
        ZStack {
            LinearGradient(gradient:
                Gradient(colors: [.blue, .black]), startPoint: .top, endPoint: .bottom)
                .edgesIgnoringSafeArea(.all)
            VStack (spacing: 30) {
                VStack {
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                    Text(countries[correctAnswerNum])
                        .foregroundColor(.white)
                        .font(.largeTitle)
                        .fontWeight(.black)
                }

                ForEach(0..<3) { number in
                    Button(action: {
                        withAnimation(.linear(duration: self.animationTime)) {
                            self.flagTapped(number)
                        }
                    }) {
                        Image(self.countries[number])
                            .renderingMode(.original)
                    }
                    .clipShape(Capsule())
                    .overlay(Capsule().stroke(Color.black, lineWidth: 1))
                    .shadow(color: .black, radius: 2)
                    .rotation3DEffect(.degrees(self.answerStatus == AnswerStatus.correct && self.tappedNum == number ? 360 : 0), axis: (x: 0, y: 1, z: 0))
                    .opacity(self.answerStatus != AnswerStatus.unanswered && self.tappedNum != number ? 0.25 : 1.0)
                    .disabled(self.answerStatus != AnswerStatus.unanswered)
                }
                VStack (alignment: .leading) {
                    Text("Number correct: \(numCorrect)")
                        .foregroundColor(.white)
                    Text("Number incorrect: \(numIncorrect)")
                        .foregroundColor(.white)
                }
                Spacer()
            }
            VStack {
                Spacer()
                HStack(spacing: 0) {
                    ForEach(0 ..< self.message.count, id: \.self) { num in
                        Text(String(self.message[num]))
                            .foregroundColor(self.answerStatus == AnswerStatus.correct ? Color.green : Color.red)
                            .font(.title)
                            .fontWeight(.black)
                            .padding(1)
                            .transition(AnyTransition.asymmetric(
                                insertion: AnyTransition.opacity.animation(Animation.easeIn.delay(Double(num) * self.animationInterval)),
                                removal: AnyTransition.opacity.animation(Animation.easeOut))
                        )
                    }
                }
            }
        }
    }

    func flagTapped(_ number: Int) {
        self.tappedNum = number
        if correctAnswerNum == number {
            numCorrect += 1
            self.answerStatus = AnswerStatus.correct
            self.message = Array(affirmations[Int.random(in: 0 ..< affirmations.count)])
            self.animationInterval = animationTime / Double(self.message.count)
            DispatchQueue.main.asyncAfter(deadline: .now() + animationTime + 1) {
                self.askQuestion(true)
            }
        } else {
            numIncorrect += 1
            answerStatus = AnswerStatus.incorrect
            self.message = Array(encouragements[Int.random(in: 0 ..< encouragements.count)])
            self.animationInterval = animationTime / Double(self.message.count)
            DispatchQueue.main.asyncAfter(deadline: .now() + animationTime + 1) {
                self.askQuestion(false)
            }
        }
    }

    func askQuestion(_ advanceToNext: Bool) {
        if advanceToNext {
            countries.shuffle()
            correctAnswerNum = Int.random(in: 0...2)
        }
        answerStatus = AnswerStatus.unanswered
        message = Array("")
    }
}

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

4      

Hey guys, on the same topic, i am having problem rotating only the right selected one. I get to rotate all the flags at the same time. Any help?

code:

struct ContentView: View {

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

    @State private var scoreTitle = ""
    @State private var score: Int = 0

    @State private var animationAmount = 0.0

    var body: some View {
        ZStack {
            LinearGradient(gradient: Gradient(colors: [.blue,.black]), startPoint: .top, endPoint: .bottom)
                .edgesIgnoringSafeArea(.all)
            VStack(spacing:30){
                VStack{
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                    //name of the Country:
                    Text(countries[correctAnswer])
                        .foregroundColor(.white)
                        .font(.largeTitle)
                        .fontWeight(.black)
                }
                ForEach(0..<3) { number in
                    Button(action: {
                             self.flagTapped(number)
                    }) {
                        Image(self.countries[number])
                            .renderingMode(.original)
                            .clipShape(Capsule())
                            .overlay(Capsule().stroke(Color.black, lineWidth:1 ))
                            .shadow(color: .black, radius: 20)
                    }
                    .rotation3DEffect(.degrees(self.animationAmount), axis: (x:0,y:1,z:0))
                    .animation(.easeInOut(duration: 2))
                }

                VStack {
                    Text("Your score is:")
                        .foregroundColor(.white)
                    Text(String(score))
                        .foregroundColor(.white)
                }
                Spacer()
            }
        }

            func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
            self.score += 1
            animationAmount += 360
        } else {
            scoreTitle = "Wrong, that is the flag of \(countries[number])"
        }
    }

3      

Is there an answer to this question? I have the same problem.

4      

I got it! The y: parameter should be

number == self.correctAnswer ? 1 : 0

5      

I got the correct flag to rotate when selected by using a custom view (i.e. Flag Image) that implemented the 3D rotation on the flag that was correct

3      

Hi,

I had a look at the proposed solutions, but I am not quite getting it. How do I delay an alert, such that the animation shows first? I am not seeing the rotation unfortunately.

Here is my code:

struct FlagImage : View {
    var imageName : String

    var body: some View {
        Image(imageName)
            .renderingMode(.original)
        .clipShape(Capsule())
            .overlay(Capsule().stroke(Color.black, lineWidth: 1))
            .shadow(color: .black, radius: 2)

    }
}

struct ContentView: View {

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

    @State private var showingScore = false
    @State private var scoreTitle = ""
    @State private var score = 0
    @State private var rotationAmount = 0.0
    @State private var tapped = false

    var body: some View {

        ZStack{
            LinearGradient(gradient: Gradient(colors: [.blue, .black]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)

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

                ForEach(0 ..< 3) { number in Button(action: {
                    self.tapped.toggle()
                    self.flagTapped(number)

                }) {
                    FlagImage(imageName: self.countries[number])
                        .rotation3DEffect(.degrees(self.rotationAmount), axis: (x: 0, y: (number == self.correctAnswer && self.tapped) ? 1 : 0, z: 0))
                        .opacity((number != self.correctAnswer && self.tapped) ? 0.25 : 1)
                    }

                }

                Text("Your score is \(score)")
                    .foregroundColor(.white)

                Spacer()
            }
        }
        .alert(isPresented: $showingScore) {
            Alert(title: Text(scoreTitle), message: Text("Your score is \(score)"), dismissButton: .default(Text("Continue")) {self.askQuestion()

                })
        }

    }
    func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
            score+=1
        } else {
            scoreTitle = "Wrong, that's the flag of \(countries[number])"
            if score > 0{
                score-=1
            }
        }
        showingScore = true
    }

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

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.