Complete Challenge 1

> Make something interesting for when the timer runs out. At the very least make some text appear, but you should also try designing a custom haptic using Core Haptics.
This commit is contained in:
CypherPoet 2020-01-22 16:46:18 -06:00
parent 83c051c34f
commit 4d9d45f3e1
3 changed files with 84 additions and 21 deletions

View File

@ -10,10 +10,31 @@ import Foundation
extension CardDeck {
var cardsArray: [Card] {
guard let cardSet = cards as? Set<Card> else { return [] }
return Array(cardSet)
}
var correctAnswerCount: Int {
cardsArray.reduce(0) { (sum, card) in
card.answerState == .correct ? sum + 1 : sum
}
}
var incorrectAnswerCount: Int {
cardsArray.reduce(0) { (sum, card) in
card.answerState == .incorrect ? sum + 1 : sum
}
}
var unansweredCount: Int {
cardsArray.reduce(0) { (sum, card) in
card.answerState == .unanswered ? sum + 1 : sum
}
}
}

View File

@ -89,6 +89,27 @@ extension CardDeckContainerView.ViewModel {
// MARK: - Computeds
extension CardDeckContainerView.ViewModel {
var isDeckEmpty: Bool { visibleCards.isEmpty }
var isTimeExpired: Bool { timeRemaining <= 0 }
var correctAnswerCountText: String {
let count = cardDeck.correctAnswerCount
return "\(count) Correct \(count == 1 ? "Answer" : "Answers")"
}
var incorrectAnswerCountText: String {
let count = cardDeck.incorrectAnswerCount
return "\(count) Wrong \(count == 1 ? "Answer" : "Answers")"
}
var unansweredCountText: String {
let count = cardDeck.unansweredCount
return "\(count) \(count == 1 ? "Card" : "Cards") Unattempted"
}
}

View File

@ -25,23 +25,28 @@ extension CardDeckContainerView: View {
ZStack {
VStack(spacing: 32) {
// 📝: It can be tricky having `CardDeckContainerView` contain the timer, because SwiftUI
// will re-render it on every tick.
// Perhaps it would be better to have `CountdownTimerView` own its timer
// and drive it with `timeRemaining`?
CountdownTimerView(
viewModel: .init(timeRemaining: self.viewModel.timeRemaining)
)
if self.viewModel.isTimeExpired {
self.timeExpirationDisplay
} else {
// 📝
// It can be tricky having `CardDeckContainerView` contain the timer,
// because SwiftUI will re-render it on every tick.
//
// Perhaps it would be better to have `CountdownTimerView` own its timer
// and drive it with `timeRemaining`?
CountdownTimerView(
viewModel: .init(timeRemaining: self.viewModel.timeRemaining)
)
CardDeckView(
width: min(max(800, geometry.size.width) * 0.8, 480),
height: min(max(800, geometry.size.width) * 0.8, 480) * 0.5,
cards: self.viewModel.visibleCards,
cardAnswered: { (answerState, index) in
self.viewModel.record(answerState, forCardAt: index)
}
)
.allowsHitTesting(self.viewModel.timeRemaining > 0)
CardDeckView(
width: min(max(800, geometry.size.width) * 0.8, 480),
height: min(max(800, geometry.size.width) * 0.8, 480) * 0.5,
cards: self.viewModel.visibleCards,
cardAnswered: { (answerState, index) in
self.viewModel.record(answerState, forCardAt: index)
}
)
}
}
HStack {
@ -50,7 +55,7 @@ extension CardDeckContainerView: View {
VStack {
Spacer()
if self.viewModel.isDeckEmpty {
if self.viewModel.isDeckEmpty || self.viewModel.isTimeExpired {
self.resetButton
} else {
self.editDeckButton
@ -89,6 +94,26 @@ extension CardDeckContainerView {
// MARK: - View Variables
extension CardDeckContainerView {
private var timeExpirationDisplay: some View {
VStack {
Text("Your time has expired.")
.font(.largeTitle)
.foregroundColor(Color("Accent3"))
.offset(y: -32)
Text("Round Summary")
.font(.title)
.padding(.bottom, 24)
VStack(alignment: .leading, spacing: 8) {
Text("") + Text(viewModel.correctAnswerCountText)
Text("🚫 ") + Text(viewModel.incorrectAnswerCountText)
Text("🤷‍♂️ ") + Text(viewModel.unansweredCountText)
}
}
}
private var editDeckButton: some View {
Button(action: {
self.viewModel.pauseRound()
@ -115,10 +140,6 @@ extension CardDeckContainerView {
// MARK: - Private Helpers
private extension CardDeckContainerView {
// func countdownFinished() {
// viewModel.pauseRound()
// }
}