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()
}
}