1
votes

in a quiz app in testView i have a question text and 4 answer buttons

i want to animate answer button background color to flash blue for 1 seconds then stop at green or red if answer is correct or wrong.

is there a way to do this in swiftui?

this is an idea of what my code looks like

struct TestView: View {
    var body: some View {
        VStack {
            Text("question")
            Spacer()

            VStack {
                Button(action: { self.validate(0) }) {
                    Text("Answer 1")
                }
                Button(action: { self.validate(1) }) {
                    Text("Answer 2")
                }
                Button(action: { self.validate(2) }) {
                    Text("Answer 3")
                }
            }
        }
    }

    private func validate(_ index: Int) {
        // check answer
    }
}
2

2 Answers

1
votes

enter image description here Here is a possible solution

import SwiftUI

struct ContentView: View {
    @State var tapped1 = false
    @State var tapped2 = false
    @State var tapped3 = false
    
    @State private var randomString = ""
    var body: some View {
        TestView(tap1: self.$tapped1, tap2: self.$tapped2, tap3: self.$tapped3)
    }
}

struct TestView: View {
    @Binding var tap1:Bool
     @Binding var tap2:Bool
     @Binding var tap3:Bool
    
    @State var answer1 = false
    @State var answer2 = true
    @State var answer3 = false
    
    
    @State private var timeSpent = 0
    @State private var timeSpent2 = 0
    @State private var timeSpent3 = 0
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    
    var body: some View {
        VStack {
            Text("question")
            Spacer()
            //Resetting the timeSpent which calculates how many seconds flow since user tapped the button
            Button(action:{
                self.timeSpent = 0
                self.tap1 = false
                self.timeSpent2 = 0
                self.tap2 = false
                self.timeSpent3 = 0
                self.tap3 = false
            })
            {
                Text("Resetting")
            }
            
            
            VStack {
                Button(action: {
                    self.validate(0)
                    self.tap1 = true
                }) {
                    HStack{
                        Text("Answer 1").foregroundColor(Color.black)
                    }.frame(width:100, height: 30)
                        .background(timeSpent >= 1 ? (answer1 ? Color.green : Color.red) : Color.blue.opacity(tap1 ? 1 : 0))
                }
                
                
                Button(action: {
                    self.validate(1)
                    self.tap2 = true
                }) {
                    HStack{
                        Text("Answer 2").foregroundColor(Color.black)
                    }.frame(width:100, height: 30)
                        .background(timeSpent2 >= 1 ? (answer2 ? Color.green : Color.red) : Color.blue.opacity(tap2 ? 1 : 0))
                }
                
                
                
                Button(action: {
                    self.validate(2)
                    self.tap3 = true
                }) {
                    HStack{
                        Text("Answer 3").foregroundColor(Color.black)
                    }.frame(width:100, height: 30)
                        .background(timeSpent3 >= 1 ? (answer3 ? Color.green : Color.red) : Color.blue.opacity(tap3 ? 1 : 0))
                }
                
                
                
                
            }.onReceive(timer) { time in
                if self.tap1 {
                    self.timeSpent += 1
            }
                
                if self.tap2 {
                   self.timeSpent2 += 1
                 }
                
                
                if self.tap3 {
                   self.timeSpent3 += 1
                  }
            }
        }
    }

    private func validate(_ index: Int) {
        // check answer
    }
}

I used timer to check how many seconds pasted since user tapped the button. You can set background color like this using nil coalescing:

  1. User didn't tap the button yet Set the random color and it's opacity to 0

  2. User tapped the button

    2-1. Second pasted less than one second -> Blue

    2-2.Second pasted more than one second

correct answer -> green, wrong answer -> red

1
votes

Another solution using DispatchQueue,

Sample

when you tap a button:

  • execute prepare function to change the first background to blue and wait one second to get the answer validation

  • on validate function get the answer solution and this action change the state of the second background

  • Finally we need to hide the first background

      import SwiftUI
    
      enum stateAns {
         case notAnswer
         case right
         case wrong
      }
    
    struct ContentView: View {
    
      @State var numAnswer : Int = 0
      @State var ans1 : stateAns = .notAnswer
      @State var ans2 : stateAns = .notAnswer
      @State var ans3 : stateAns = .notAnswer
    
     func prepate(numAnswer: Int) {
         self.numAnswer = numAnswer
         self.validate(ans: 0) //Reset questions if you need
         DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.validate(ans: numAnswer) }
      }
    
      func validate (ans: Int)  {
    
          switch(ans) {
             case 1 : self.ans1 = .right
             case 2 : self.ans2 = .wrong
             case 3 : self.ans3 = .right
             default: self.ans1 = .notAnswer; self.ans2 = .notAnswer; self.ans3 = .notAnswer;
          }
    
    
       }
    
       var body: some View {
    
          VStack {
             Text("question")
             Spacer()
    
             VStack {
                Button(action: { self.prepate(numAnswer: 1)}) {
                  Text("Answer 1")
                      .background((self.numAnswer == 1 ? Color.blue : Color.white).opacity(self.ans1 == .notAnswer ? 1 : 0))
                      .background(self.ans1 == .notAnswer ? Color.white : (self.ans1 == .right ? Color.green : Color.red) )
              }
                 Button(action: { self.prepate(numAnswer: 2)}) {
                    Text("Answer 2")
                        .background((self.numAnswer == 2 ? Color.blue : Color.white).opacity(self.ans2 == .notAnswer ? 1 : 0))
                        .background(self.ans2 == .notAnswer ? Color.white : (self.ans2 == .right ? Color.green : Color.red) )
    
                  }
                  Button(action: { self.prepate(numAnswer: 3)}) {
                     Text("Answer 3")
                        .background((self.numAnswer == 3 ? Color.blue : Color.white).opacity(self.ans3 == .notAnswer ? 1 : 0))
                        .background(self.ans3 == .notAnswer ? Color.white : (self.ans3 == .right ? Color.green : Color.red) )
    
    
                  }
              }
          }
      }
    }