Home > database >  Changing backgroundColor to green if correct, to red if wrong in a quiz. “Cannot find 'layer�
Changing backgroundColor to green if correct, to red if wrong in a quiz. “Cannot find 'layer�

Time:04-18

Cannot figure out how to make a short-time change of a color on an answer option cell (not button!), green or red depending on correctness or falsity of the answer.

Searched through the internet, watched some videos, but they are not fully applicable for my case.

E.g. it works inside viewDidLoad(), as in an example on Youtube, it’s possible to add the animation view.layer.add(animation, forKey: “backgroundColor”).

But particularly in my case, I put these methods inside a struct, and it doesn’t define any layers, the error shows up “Cannot find 'layer' in scope”.

So with my very little knowledge of Swift I think that this feature doesn’t work, because I don’t know how to add this animation.

But maybe I am doing it completely wrong at all and it should be done in a completely different way?

import UIKit

struct Answer {
  let text: String
  let correct: Bool // true/false
  
  func turnGreen() {
    let go_green = CASpringAnimation(keyPath: "backgroundColor")
    go_green.fromValue = UIColor.white.cgColor
    go_green.toValue = UIColor.green.cgColor
    go_green.duration = 2
    go_green.autoreverses = false
    go_green.repeatCount = 1
    go_green.initialVelocity = 300
    
    layer.add(go_green, forKey: "backgroundColor")
  }
  
  func turnRed() {
    let go_red = CABasicAnimation(keyPath: "backgroundColor")
    go_red.duration = 3
    go_red.fromValue = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
    go_red.toValue = UIColor.red.cgColor
    go_red.autoreverses = false
    go_red.repeatCount = 1
  //  go_red.initialVelocity = 3
    
    layer.add(go_red, forKey: "backgroundColor")
  }
}

And then I intended to put these turnGreen()/turnRed() methods into private func checkAnswer(for answer: Answer) in ViewController.

  private func checkAnswer(for answer: Answer) {
    if answer.correct {
      answer.turnGreen()
      if questionNumber > questions.count {
        // filling the scale fully
        progressView.progress = Float(questions.count)
        score  = 1
        scoreLabel.text = "Score: \(score)"
        answer.turnGreen()
        
              let alert = UIAlertController(title: "Awesome",
                                            message: "End of Quiz. Do you want to start over?",
                                            preferredStyle: .alert)
              let restartAction = UIAlertAction(title: "Restart",
                                                style: .default,
                                                handler: { action in self.shuffleQuestions() } )
              alert.addAction(restartAction)
              present(alert, animated: true, completion: nil)
        return
      }
      answer.turnGreen()
      score  = 1
      showQuestion()
    } else {
      // wrong
      
      if questionNumber > questions.count {
        // filling the scale fully
        progressView.progress = Float(questions.count)
        scoreLabel.text = "Score: \(score)"
        
              let alert = UIAlertController(title: "Awesome",
                                            message: "End of Quiz. Do you want to start over?",
                                            preferredStyle: .alert)
              let restartAction = UIAlertAction(title: "Restart",
                                                style: .default,
                                                handler: { action in self.shuffleQuestions() } )
              alert.addAction(restartAction)
              present(alert, animated: true, completion: nil)
        return
      }
      answer.turnRed()
      showQuestion()
    }
  }

CodePudding user response:

First of all your Answer model should be like this.

struct Answer {
    let text: String
    let correct: Bool
}

Create a custom UITableViewCell and add turnGreen() and turnRed() methods to it.

class CustomCell: UITableViewCell {
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
    func turnGreen() {
        let go_green = CASpringAnimation(keyPath: "backgroundColor")
        go_green.fromValue = UIColor.white.cgColor
        go_green.toValue = UIColor.green.cgColor
        go_green.duration = 2
        go_green.autoreverses = false
        go_green.repeatCount = 1
        go_green.initialVelocity = 300
        
        contentView.layer.add(go_green, forKey: "backgroundColor")
    }
    
    func turnRed() {
        let go_red = CABasicAnimation(keyPath: "backgroundColor")
        go_red.duration = 3
        go_red.fromValue = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
        go_red.toValue = UIColor.red.cgColor
        go_red.autoreverses = false
        go_red.repeatCount = 1
        //  go_red.initialVelocity = 3
        
        contentView.layer.add(go_red, forKey: "backgroundColor")
    }
}

Then register this cell to tableView in viewDidLoad() method.

override func viewDidLoad() {
    super.viewDidLoad()    
    
    tableView.delegate = self
    tableView.dataSource = self
    tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}

Change the implementation of cellForRowAt and didSelectRow methods like below

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as? CustomCell else {
        return UITableViewCell()
    }
    
    cell.textLabel?.text = answers[indexPath.row].text
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    
    guard let cell = tableView.cellForRow(at: indexPath) as? CustomCell else { return }
    
    let answer = answers[indexPath.row]
    if answer.correct {
        score  = 1
        cell.turnGreen()
    } else {
        cell.turnRed()
    }
    
    DispatchQueue.main.asyncAfter(deadline: .now() 1) {
        self.updateUI()
    }
}

I have renamed the method checkAnswer to updateUI. Here is the code.

private func updateUI() {
    progressView.progress = Float(score/questions.count)
    scoreLabel.text = "Score: \(score)"
    
    questionNumber  = 1
    
    if questionNumber > questions.count {
        let alert = UIAlertController(title: "Awesome",
                                      message: "End of Quiz. Do you want to start over?",
                                      preferredStyle: .alert)
        let restartAction = UIAlertAction(title: "Restart",
                                          style: .default,
                                          handler: { action in self.shuffleQuestions() } )
        alert.addAction(restartAction)
        present(alert, animated: true, completion: nil)
    } else {
        showQuestion()
    }
}
  • Related