Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to animate particular button from ForEach in SwiftUI?

I have a problem with animating buttons. I have them created using ForEach method. The layout of my app consists of 3 flags. We have text that is saying that we have to choose a flag of particular country and if we choose the correct one we gain points.

I want to animate particular button when it is pressed. Doesn't matter if the answer is correct or not.

My code for creating these buttons looks like this:

ForEach(0 ..< 3) { number in
    Button(action: {
        withAnimation {
            if number == self.correctAnswer {
                self.animationAmount += 360
            }
        }
        
        self.flagTapped(number)
    }) {
        ImageView(myImage: self.countries[number])
    }
    .rotation3DEffect(.degrees(number == self.correctAnswer ? self.animationAmount : 0), axis: (x: 0, y: 1, z: 0))
    .opacity(self.showAnimation && number != self.correctAnswer ? 0.25 : 1)
    .background(self.showAnimation && number != self.correctAnswer ? Color.red: nil)
}

self.flagTapped(number) toggles showAnimation to be true.

like image 330
Michał Śniady Avatar asked Dec 05 '25 08:12

Michał Śniady


2 Answers

Here is possible alternate approach - more appropriate looks to move button into separated view and let it animate itself when needed.

Demo prepared on some replicated simplified code (due to absent parts in provided snapshot). Tested with Xcode 12 / iOS 14.

demo

struct DemoView: View {
    let countries = ["flag-1", "flag-2", "flag-3", "flag-4"]

    @State private var number: Int = -1
    let correctAnswer = 1
    var body: some View {
        VStack {
            ForEach(0 ..< 3) { number in
                FlagButton(number: number, image: self.countries[number], answer: correctAnswer){
                    self.flagTapped(number)
                }
            }
        }
    }

    private func flagTapped(_ number: Int) {
        self.number = number
    }
}


struct FlagButton: View {
    let number: Int
    let image: String
    let answer: Int
    var showAnimation = false
    let action: () -> ()

    @State private var animationAmount = Double.zero
    var body: some View {
        Button(action: {
            if self.number == self.answer {
                self.animationAmount += 360
            }
            action()
        }) {
            Image(image)
        }
        .rotation3DEffect(.degrees(number == answer ? self.animationAmount : 0), axis: (x: 0, y: 1, z: 0))
        .opacity(self.showAnimation && number != answer ? 0.25 : 1)
        .background(self.showAnimation && number != answer ? Color.red: nil)
        .animation(.default)
    }
}
like image 60
Asperi Avatar answered Dec 07 '25 23:12

Asperi


@State private var selectedFlag = -1 //Add this in your ContentView struct
ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                        noOfQuestions += 1
                        if noOfQuestions == 8 {
                            showingEnd = true
                        }
                        if scoreTitle == "Correct Answer" {
                            score += 1
                        }
                    } label: {
                        Image(countries[number])
                            .renderingMode(.original)
                            .clipShape(Capsule())
                            .shadow(radius: 5)
                            .rotation3DEffect(.degrees(selectedFlag == number ? 360 : 0),
                                              axis: (x: 0, y:1, z:0))
                            .opacity((selectedFlag == number || selectedFlag == -1) ? 1 : 0.25)
                            .animation(.default, value: selectedFlag )
                            .scaleEffect((selectedFlag == number || selectedFlag == -1) ? 1 : 0.75)
                    }
                }

The selectedFlag var will help determine which of the flags is in currently chosen and applying animations to the specific images rather than the buttons works best.

like image 21
Gaurav Ganju Avatar answered Dec 07 '25 22:12

Gaurav Ganju



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!