M5L10 : Button Logic

Today’s lesson (Module 5, Lesson 10, of CodeWithChris‘ iOS Foundations) deals with disabling buttons after an answer has been submitted, and changing the button colours if the answer is correct/incorrect.

I have to admit, that this is the kind of coding that really appeals to me – logic. I felt that I should have been just as overwhelmed by the coding today as always but, oddly, I wasn’t. Not only did I follow it, but my own sense of logic had me a couple of steps ahead of Chris.

When the app presents a quiz, the answers exist in one of four states.

  • No answer has been selected (Submit button is disabled),
  • Answer has been selected, but not submitted (Submit button is enabled, selected answer turns grey),
  • Answer submitted but is wrong (selected answer turns red, correct answer turns green, other options are disabled),
  • Answer submitted is correct (selected answer turns green, other options are disabled).

This is the provided solution:

...
Button {
    selectedAnswerIndex = index
} label: {
    ZStack {
        if submitted == false { // Answer not yet submitted
            RectangleCard(color: index == selectedAnswerIndex ? .gray : .white )
                .frame(height: 48)
        }
        else { // Answer has been submitted
            if index == selectedAnswerIndex && index == model.currentQuestion!.correctIndex {
                // User has selected the right answer : Show a green background
                RectangleCard(color: Color.green)
                    .frame(height: 48)
            }
            else if index == selectedAnswerIndex && index != model.currentQuestion!.correctIndex {
                // User has selected the wrong answer : Show a red background
                RectangleCard(color: Color.red)
                    .frame(height: 48)
            }
            else if index == model.currentQuestion!.correctIndex {                                    
                // This button is the correct answer : Show a green background
                RectangleCard(color: Color.green)
                    .frame(height: 48)
            }
            else {
                RectangleCard(color: Color.white)
                    .frame(height: 48)
            }
        }
        Text(model.currentQuestion!.answers[index])
    }
 }
.disabled(submitted)
...

Towards the end, Chris does suggest that if there wasn’t an argument for keeping the code as understandable as possible for newcomers, the first and third else{} conditions could be combined, re:

...
Button {
    selectedAnswerIndex = index
} label: {
    ZStack {
        if submitted == false { // Answer not yet submitted
            RectangleCard(color: index == selectedAnswerIndex ? .gray : .white )
                .frame(height: 48)
       }
        else { // Answer has been submitted
            if (index == selectedAnswerIndex && 
                index == model.currentQuestion!.correctIndex) ||
                index == model.currentQuestion!.correctIndex {
                // User has selected the right answer, or this is the correct answer anyway : Show a green background
                RectangleCard(color: Color.green)
                    .frame(height: 48)
            }
            else if index == selectedAnswerIndex && index != model.currentQuestion!.correctIndex {
                // User has selected the wrong answer : Show a red background
                RectangleCard(color: Color.red)
                    .frame(height: 48)
            }
            /* else if index == model.currentQuestion!.correctIndex {                                    
                // This button is the correct answer : Show a green background
                RectangleCard(color: Color.green)
                    .frame(height: 48)
            } */
            else {
                RectangleCard(color: Color.white)
                    .frame(height: 48)
            }
        }
        Text(model.currentQuestion!.answers[index])
    }
 }
.disabled(submitted)
...

For the sake of clarity (understandble), Chris chose not to go with that option.

However, it occurs to my sense of logic that we want the button with the “correct” answer to turn green regardless – it doesn’t matter if the user selected it or not. So my solution would be:

...
Button {
    selectedAnswerIndex = index
} label: {
     ZStack {
        if submitted == false { // Answer not yet submitted
            RectangleCard(color: index == selectedAnswerIndex ? . gray : .white)
                .frame(height:48)
        }
        else { // Answer has been submitted
            if index == model.currentQuestion!.correctIndex { 
                RectangleCard(color: .green)
                    .frame(height:48)
            }
            else if index == selectedAnswerIndex {
                RectangleCard(color: .red)
                    .frame(height:48)
            }
            else { 
                RectangleCard(color: .white)
                    .frame(height:48)
            }
        }
        Text(model.currentQuestion!.answers[index])
            .foregroundColor(.black) // without this, text will turn grey when button is disabled
    }
}
.disabled(submitted)
...

My solution reduces the number of conditions down to simple steps (after the user has submitted their selection):

  1. If the button has the correct answer, turn it green. It doesn’t matter if the user selected it or not, so we don’t need any other conditions;
  2. If the button has the wrong answer (i.e., it failed the previous condition), and the user has selected it, turn it red. We don’t need the && index != model.currentQuestion!.correctIndex condition because that condition has already been satisfied by virtue of it failing the previous condition.
  3. We don’t need the 3rd condition to show the green background, because it’s already been handled by the first condition.
  4. The final condition keeps any other button that hasn’t been affected by the previous conditions the same as it was before.

For me, this keeps the code simpler and clearer because it reduces the number of conditions that need to be deciphered by both the the App, and anyone viewing the code. So I’m sticking with this version until something comes up that it conflicts with.

It’s a relief and reassuring that my sense of logic still persists into my ever-nearing dotage!