简体   繁体   English

SwiftUI - 按下按钮时动画视图

[英]SwiftUI - Animate a view on button press

I have just finished lesson 6 of #hackingwithswift.我刚刚完成了#hackingwithswift 的第 6 课。 I'm going mad over a challenge that shouldn't be that hard at all...我正在为一个根本不应该那么难的挑战而生气......

Here I have a working game called GuessTheFlag wherein the user must select the correct flag indicated then receives an alert before being passed to the next round.在这里,我有一个名为 GuessTheFlag 的工作游戏,其中用户必须选择指示的正确标志,然后在传递到下一轮之前收到警报。

My problem is simple: I would like to have the incorrect flag views become opaque and have the correct flag view rotate 360 degrees when an answer is chosen.我的问题很简单:我想让不正确的标志视图变得不透明,并且在选择答案时让正确的标志视图旋转 360 度。 This should all happen behind the alert until the user clicks 'continue'.这应该全部发生在警报之后,直到用户单击“继续”。

Optional: I would also like to remove the alert altogether and have the game move on to the next round after the rotating animation has finished.可选:我还想完全删除警报,并在旋转动画完成后让游戏进入下一轮。 Can I bind the round change to the end of the animation?我可以将圆形更改绑定到动画的结尾吗? Or do I have to use some sort of sleep() command?还是我必须使用某种 sleep() 命令?

import SwiftUI

struct FlagView: View {
    var flag: String
    
    var body: some View {
        Image(flag)
            .renderingMode(.original)
            .clipShape(Capsule())
            .overlay(Capsule().stroke(Color.black, lineWidth: 2))
            .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 private var correctAnswer = Int.random(in: 0...2)
    @State private var showingScore = false
    @State private var scoreTitle = ""
    @State private var score = 0
    @State private var animate = false
    
    var alert: Alert {
        if scoreTitle == "Wrong" {
            return Alert(title: Text(scoreTitle), message: Text("The correct answer was \(countries[correctAnswer])"), dismissButton: .default(Text("Restart")) {
                self.askQuestion()
                animate = false
            })
        }
        else {
            return Alert(title: Text(scoreTitle), message: Text("Your Score is \(score)"), dismissButton: .default(Text("Continue")) {
                self.askQuestion()
                animate = false
            })
        }
        
    }

    var body: some View {
        ZStack{
            LinearGradient(gradient: Gradient(colors: [.black,.blue, .orange]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)
            
            VStack(spacing: 40) {
                VStack{
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                    Text(countries[correctAnswer])
                        .font(.largeTitle)
                        .fontWeight(.black)
                        .foregroundColor(.white)
                }
                
                ForEach(0 ..< 3){ number in
                    Button(action: {
                        withAnimation {
                            self.flagTapped(number)
                            animate.toggle()
                        }
                    }) {
                        FlagView(flag: self.countries[number])
                    }
                }
                
                Text("Your Score is \(score)")
                    .font(.headline)
                    .fontWeight(.black)
                    .foregroundColor(Color.white)
                
                Spacer()
            }
        }
        .alert(isPresented: $showingScore) {
            alert
        }
    }

    func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
            score += 1
            
        } else {
            scoreTitle = "Wrong"
            score = 0
            
        }
        
        showingScore = true
        
    }
    
    func askQuestion() {
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
    }
}


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

Any help would be appreciated!任何帮助,将不胜感激! Thanks in advance提前致谢

Just apply the view modifier opacity and rotationEffect on FlagView and instead of using discrete values (such as .opacity(0.1) ) make it conditional:只需在FlagView上应用视图修饰符opacityrotationEffect而不是使用离散值(例如.opacity(0.1) )使其成为有条件的:

.opacity((self.animate && number != correctAnswer) ? 0.1 : 1.0)

The animation part will be automatically done be SwiftUI.动画部分将通过 SwiftUI 自动完成。 Please see my working example below:请参阅下面的我的工作示例:

import SwiftUI

struct FlagView: View {
    var flag: String
    
    var body: some View {
        Text(flag)
    }
    
}

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 score = 0
    @State private var animate = false
    
    var alert: Alert {
        if scoreTitle == "Wrong" {
            return Alert(title: Text(scoreTitle), message: Text("The correct answer was \(countries[correctAnswer])"), dismissButton: .default(Text("Restart")) {
                self.askQuestion()
            })
        }
        else {
            return Alert(title: Text(scoreTitle), message: Text("Your Score is \(score)"), dismissButton: .default(Text("Continue")) {
                self.askQuestion()
            })
        }
        
    }

    var body: some View {
        ZStack{
            LinearGradient(gradient: Gradient(colors: [.black,.blue, .orange]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)
            
            VStack(spacing: 40) {
                VStack{
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                    Text(countries[correctAnswer])
                        .font(.largeTitle)
                        .fontWeight(.black)
                        .foregroundColor(.white)
                }
                
                ForEach(0 ..< 3){ number in
                    Button(action: {
                        withAnimation(.easeInOut(duration: 0.5)) {
                            self.animate = true
                        }
                        self.flagTapped(number)
                    }) {
                        FlagView(flag: self.countries[number])
                            .opacity((self.animate && number != correctAnswer) ? 0.1 : 1.0)
                            .rotationEffect(.init(degrees: (self.animate && number == correctAnswer) ? 360.0 : 0.0))
                    }
                }
                
                Text("Your Score is \(score)")
                    .font(.headline)
                    .fontWeight(.black)
                    .foregroundColor(Color.white)
                
                Spacer()
            }
        }
        .alert(isPresented: $showingScore) {
            alert
        }
    }

    func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
            score += 1
            
        } else {
            scoreTitle = "Wrong"
            score = 0
            
        }
        
        showingScore = true
        
    }
    
    func askQuestion() {
        self.animate = false
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
    }
}


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

For your optional question you could introduce a custom modifier (eg as described here ).为了您的选购问题(如例如,你可以引入一个自定义的修改在这里)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM