skip to Main Content

Just getting started with SwiftUI so there is probably something straightforward I am missing.

When the "CHECK" button is pressed, I want to change the background color of the button with an index that matches question.correctChoiceIndex, as well as the button selected by the user, if it is not the correct one.

I am not sure how to actually reference the buttons with a function (if that is the best way), and I figured it might be difficult because the buttons are made with the AnswerButton struct.

Here is my code


import SwiftUI

struct ContentView: View {
    let question: Question
   @State var guessedIndex: Int? = nil
    
    var body: some View {
        
        VStack{
            Spacer()
            Text(question.questionText)
                .padding()
           
                
        VStack {
            ForEach(question.AnswerChoices.indices) {index in
                AnswerButton(text: question.AnswerChoices[index]){ 
                    guessedIndex = index
                }
                .border(selectChoice(at: index), width: 4)
            }}
            Spacer()
            Text("Answer feedback")
                .padding()
            Spacer()
        HStack{
            Button("CHECK") {
               
            }
            .padding()
            Button("NEXT") {
                /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/ /*@END_MENU_TOKEN@*/
            }
            .padding()
        }
        }
        
    }
    func selectChoice(at buttonIndex: Int) -> Color {
        if buttonIndex == guessedIndex {
            return .gray

    }
        else {
            return .clear
        }
    }
    
}


struct AnswerButton: View {
    let text: String
    let onClick: () -> Void
    var body: some View {
        Button(action: {
            onClick()
        }) {
                Text(text)

            }
        .padding()
        .background(Color.yellow)
    }

}
               

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ContentView(question: Question.AllQuestions[0])
           
        }
    }
}


I thought looping through all the buttons in the view and checking their index could work, but it also seems a bit inefficient to do.

2

Answers


  1. In SwiftUI you do not reference Buttons etc…, you change the state of the View.

    You could try this approach, in keeping with your initial code base.

    Note there are many other ways to achieve what you want, in particular
    I recommend you have a look at this link, it gives you examples of how to manage data in your app:
    https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

    // for testing
    struct Question: Identifiable {
        let id = UUID()
        var questionText: String
        var answerChoices: [String]
    }
     
    struct ContentView: View {
        // for testing
        let question: Question = Question(questionText: "some question", answerChoices: ["answer-1","answer-2","answer-3"])
        
        @State var guessedIndex: Int? = nil
        @State var isCorrect: Bool = false // <-- here
        
        var body: some View {
            VStack{
                Spacer()
                Text(question.questionText).padding()
                VStack {
                    ForEach(question.answerChoices.indices, id: .self) { index in  // <-- here
                        AnswerButton(text: question.answerChoices[index]) {
                            guessedIndex = index
                            isCorrect = false // <-- here
                        }
                        .border(guessedIndex == index ? .green : .clear, width: 4) // <-- here
                        .background(guessedIndex == index && isCorrect ? .gray : .yellow) // <-- here
                    }
                }
                Spacer()
                Text("Answer feedback").padding()
                Spacer()
                HStack{
                    Button("CHECK") {
                        checkAnswer() // <-- here
                    }.padding()
                    Button("NEXT") {
                        
                    }.padding()
                }
            }
        }
        
        func checkAnswer() {
            // some logic here to determine which answer is correct
            isCorrect = guessedIndex == 1  // for testing, answer-2
        }
    }
    
    struct AnswerButton: View {
        let text: String
        let onClick: () -> Void
        
        var body: some View {
            Button(action: {
                onClick()
            }) {
                Text(text)
            }
            .padding()
        }
    }
    
    Login or Signup to reply.
  2. Had to make some assumptions about the Question, and there are better ways one could structure this, but here’s something that works.

    This will mark an incorrect answer as red if selected and checked, and will mark the correct answer as green.

    You would need to likely disable the buttons or progress after the check as well.

    import SwiftUI
    
    struct Question {
        let questionText: String
        let answerChoices: [String]
        let correctAnswerIndex: Int
    }
    
    struct ContentView: View {
        
        let question: Question
        @State var guessedIndex: Int? = nil
        @State var didCheck = false
        
        var body: some View {
            
            VStack {
                Spacer()
                Text(question.questionText)
                    .padding()
                
                ForEach(0 ..< question.answerChoices.count) { index in
                    let answer = question.answerChoices[index]
                    AnswerButton(text: answer,
                                 isCorrectAnswer: index == question.correctAnswerIndex,
                                 didCheck: didCheck,
                                 isSelected: index == guessedIndex) {
                        guessedIndex = index
                    }
                }
                Spacer()
                Text("Answer feedback")
                    .padding()
                Spacer()
                HStack{
                    Button("CHECK") {
                        didCheck = true
                    }
                    .padding()
                    Button("NEXT") {
                        
                    }
                    .padding()
                }
            }
            
        }
        
    }
    
    
    struct AnswerButton: View {
        
        let text: String
        let isCorrectAnswer: Bool
        let didCheck: Bool
        let isSelected: Bool
        let onClick: () -> Void
        
        var body: some View {
            Button(text, action: onClick)
                .padding()
                .border(isSelected ? .gray : .clear)
                .background(backgroundColorForCurrentState())
        }
        
        func backgroundColorForCurrentState() -> Color {
            switch (didCheck, isCorrectAnswer, isSelected) {
            case (true, false, true):
                return .red
                
            case (true, true, _):
                return .green
                
            case (_, _, _):
                return .yellow
            }
        }
        
        
    }
    
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            Group {
                ContentView(question: Question(questionText: "examnple",
                                               answerChoices: ["one", "two", "three"],
                                               correctAnswerIndex: 1))
                
            }
        }
    }
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search