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

SOLVED: Animation Challenge #1

Forums > 100 Days of SwiftUI

I'm working on Animation Challenge #1. The ask is to modify the Guess the Flag project so that when you tap a flag it spins around 360 degrees on the Y axis.

Seems easy enough but when I try to add an animation modifier to button the in Guess the Flag (which I put before the label code), I get the following error: Trailing closure passed to parameter of type 'PrimitiveButtonStyleConfiguration' that does not accept a closure.

To try an figure out why I was getting the error I removed the animation modifier and commented out the label code and get that error as well.

Can someone explain why the error and why I can't add an animation modifier to the button?

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 = ""
    @State private var userScore = 0
    @State private var numberOfTaps = 1
    @State private var lastQuestion = false
    @State private var animationAmount = 2.0

    var body: some View {
        ZStack {
            /*LinearGradient(gradient: Gradient(colors: [.green, .gray, .blue]), startPoint: .top, endPoint: .bottom)
                    .ignoresSafeArea()*/

            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: 400)
                .ignoresSafeArea()
            VStack  {
                Spacer()
                Text("Guess the Flag")
                    .viewTitleStyle()
                //.font(.largeTitle.bold())
                    //.foregroundColor(.white)

                VStack(spacing: 15) {
                    VStack {
                        Text("Tap the flag of ")
                            .font(.subheadline.weight(.medium))
                            .foregroundStyle(.secondary)
                        Text(countries[correctAnswer])
                            .font(.largeTitle.weight(.semibold))
                            //.foregroundColor(.white)
                    }
                    ForEach(0..<3) {number in
                        Button {
                            flagTapped(number)
                        } .animation(.easeIn, value: animationAmount)
                        /*label: {
                            FlagImage(image: countries[number])
                                //.renderingMode(.original)
                                //.clipShape(Capsule())
                                //.shadow(radius: 5)
                            }*/
                    }
                }
                .frame(maxWidth: .infinity)
                    .padding(.vertical, 20)
                    .background(.regularMaterial)
                    .clipShape(RoundedRectangle(cornerRadius: 20))
                Spacer()
                Spacer()
                Text("Score: \(userScore)")
                    .foregroundColor(.white)
                    .font(.title.bold())
                Spacer()
            }
            .padding()
        }
        .alert(scoreTitle, isPresented: $showingScore) {
            Button("Continue", action: askQuestion)
        } message: {
            Text("Your score is now \(userScore)")
        }
        .alert("Game Over", isPresented: $lastQuestion) {
                    Button("Restart Game", action: resetScore)
                } message: {
                    if userScore > 6 {
                        Text("Your final score is \(userScore). Great job!")
                    } else if userScore > 4 {
                        Text("Your final score is \(userScore). Not bad")
                    } else {
                        Text("Your final score is \(userScore). You need to study a bit")
                    }
                }
    }

    func flagTapped(_ number: Int) {
        if number == correctAnswer && numberOfTaps < 8 {
            scoreTitle = "Correct"
            userScore += 1
            numberOfTaps += 1
            showingScore = true
        } else if number != correctAnswer && numberOfTaps < 8 {
            scoreTitle = "Wrong. That's the flag of  \(countries[number]). You lose a point."
            userScore -= 1
            numberOfTaps += 1
            showingScore = true
        } else if number == correctAnswer {
            scoreTitle = "Correct"
            userScore += 1
            lastQuestion =  true
        } else {
            scoreTitle = "Wrong. That's the flag of  \(countries[number]). You lose a point."
            userScore -= 1
            lastQuestion = true
        }
    }
    func askQuestion() {
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
    }
    func resetScore() {
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
        userScore = 0
        numberOfTaps = 0
    }
}

struct FlagImage: View {
    var image: String

    var body: some View {
        Image(image)
            .renderingMode(.original)
            .clipShape(Rectangle())
            .shadow(radius: 5)
    }
}

struct ViewTitle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.largeTitle)
            .foregroundColor(.white)
    }
}

extension View {
    func viewTitleStyle() -> some View {
        modifier(ViewTitle())
    }
}

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

   

You have placed the animation modifier in the wrong place; it is between two parameters of your Button.

You are using this initializer on Button:

init(action: @escaping () -> Void, label: () -> Label)

which looks like this in use:

Button(action: {
    //this is the action closure
}, label: {
    //this is the label closure
})

or, with multiple trailing closures:

Button {
    //this is the action closure
} label: {
    //this is the label closure
}

You can't put a modifer in between the two parameters action and label. You either need to put it, depending on what the modifier is doing, after both parameters:

Button {
    //this is the action closure
} label: {
    //this is the label closure
}
.modifier(...)

or on something inside the label parameter:

Button {
    //this is the action closure
} label: {
    //this is the label closure
    Text("or whatever")
        .modifier(...)
}

   

Much appreciate @roosterboy.

   

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.