Implement round timer functionality and add a deck reset button
This commit is contained in:
parent
65b99570dc
commit
b5cd26488c
|
@ -40,9 +40,10 @@ extension CardDeckContainerView {
|
||||||
@Published var cards: [Card] = []
|
@Published var cards: [Card] = []
|
||||||
@Published var timeRemaining: TimeInterval
|
@Published var timeRemaining: TimeInterval
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
init(
|
init(
|
||||||
roundDuration: TimeInterval = 10.0
|
roundDuration: TimeInterval = 100.0
|
||||||
) {
|
) {
|
||||||
self.roundDuration = roundDuration
|
self.roundDuration = roundDuration
|
||||||
self.timeRemaining = roundDuration
|
self.timeRemaining = roundDuration
|
||||||
|
@ -80,10 +81,11 @@ extension CardDeckContainerView.ViewModel {
|
||||||
|
|
||||||
// MARK: - Computeds
|
// MARK: - Computeds
|
||||||
extension CardDeckContainerView.ViewModel {
|
extension CardDeckContainerView.ViewModel {
|
||||||
|
|
||||||
var timeRemainingText: String {
|
var timeRemainingText: String {
|
||||||
NumberFormatters.cardCountdown.string(for: timeRemaining) ?? ""
|
NumberFormatters.cardCountdown.string(for: timeRemaining) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isDeckEmpty: Bool { cards.isEmpty }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,6 +97,18 @@ extension CardDeckContainerView.ViewModel {
|
||||||
try? fetchedResultsController.performFetch()
|
try? fetchedResultsController.performFetch()
|
||||||
cards = extractResults(from: fetchedResultsController)
|
cards = extractResults(from: fetchedResultsController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func resetDeck() {
|
||||||
|
fetchCards()
|
||||||
|
self.timeRemaining = roundDuration
|
||||||
|
self.isTimerActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func pauseRound() {
|
||||||
|
isTimerActive = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,7 +130,7 @@ private extension CardDeckContainerView.ViewModel {
|
||||||
|
|
||||||
CurrentApp.notificationCenter
|
CurrentApp.notificationCenter
|
||||||
.publisher(for: UIApplication.willEnterForegroundNotification)
|
.publisher(for: UIApplication.willEnterForegroundNotification)
|
||||||
.map { _ in true }
|
.map { _ in self.isDeckEmpty == false }
|
||||||
.assign(to: \.isTimerActive, on: self)
|
.assign(to: \.isTimerActive, on: self)
|
||||||
.store(in: &subscriptions)
|
.store(in: &subscriptions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ import CypherPoetSwiftUIKit
|
||||||
|
|
||||||
|
|
||||||
struct CardDeckContainerView {
|
struct CardDeckContainerView {
|
||||||
@Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
|
|
||||||
|
|
||||||
@ObservedObject var viewModel: ViewModel = .init()
|
@ObservedObject var viewModel: ViewModel = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +21,8 @@ extension CardDeckContainerView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack {
|
VStack(spacing: 22) {
|
||||||
Text("Time Remaining: \(self.viewModel.timeRemaining)")
|
Text("Time Remaining: \(self.viewModel.timeRemainingText)")
|
||||||
.font(.largeTitle)
|
.font(.largeTitle)
|
||||||
.foregroundColor(Color.yellow)
|
.foregroundColor(Color.yellow)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
@ -39,19 +37,22 @@ extension CardDeckContainerView: View {
|
||||||
onRemove: { (card, index) in self.cardRemoved(at: index) }
|
onRemove: { (card, index) in self.cardRemoved(at: index) }
|
||||||
)
|
)
|
||||||
.allowsHitTesting(self.viewModel.timeRemaining > 0)
|
.allowsHitTesting(self.viewModel.timeRemaining > 0)
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if self.differentiateWithoutColor {
|
if self.viewModel.isDeckEmpty {
|
||||||
VStack {
|
VStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
HStack {
|
||||||
self.swipeDirectionIndicators
|
Spacer()
|
||||||
|
self.resetButton
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
.padding()
|
||||||
.background(Color("CardDeckBackground"))
|
.background(Color("CardDeckBackground"))
|
||||||
.edgesIgnoringSafeArea(.all)
|
.edgesIgnoringSafeArea(.all)
|
||||||
}
|
}
|
||||||
|
@ -66,24 +67,12 @@ extension CardDeckContainerView {
|
||||||
// MARK: - View Variables
|
// MARK: - View Variables
|
||||||
extension CardDeckContainerView {
|
extension CardDeckContainerView {
|
||||||
|
|
||||||
private var swipeDirectionIndicators: some View {
|
private var resetButton: some View {
|
||||||
HStack {
|
Button("Start Again", action: viewModel.resetDeck)
|
||||||
Image(systemName: "xmark.circle")
|
.padding()
|
||||||
.padding()
|
.background(Color.white)
|
||||||
.background(Color.black.opacity(0.7))
|
.foregroundColor(.black)
|
||||||
.clipShape(Circle())
|
.clipShape(Capsule())
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
|
|
||||||
Image(systemName: "checkmark.circle")
|
|
||||||
.padding()
|
|
||||||
.background(Color.black.opacity(0.7))
|
|
||||||
.clipShape(Circle())
|
|
||||||
}
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.font(.largeTitle)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +83,10 @@ extension CardDeckContainerView {
|
||||||
private extension CardDeckContainerView {
|
private extension CardDeckContainerView {
|
||||||
func cardRemoved(at index: Int) {
|
func cardRemoved(at index: Int) {
|
||||||
viewModel.cards.remove(at: index)
|
viewModel.cards.remove(at: index)
|
||||||
|
|
||||||
|
if viewModel.isDeckEmpty {
|
||||||
|
viewModel.pauseRound()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +98,6 @@ struct CardDeckContainerView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
CardDeckContainerView()
|
CardDeckContainerView()
|
||||||
.environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext)
|
.environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext)
|
||||||
// .environment(\.accessibilityDifferentiateWithoutColor, .constant(true))
|
// .previewLayout(PreviewLayout.iPhone11Landscape)
|
||||||
.previewLayout(PreviewLayout.iPhone11Landscape)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
struct CardDeckView {
|
struct CardDeckView {
|
||||||
|
@Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
|
||||||
|
|
||||||
var width: CGFloat
|
var width: CGFloat
|
||||||
var height: CGFloat
|
var height: CGFloat
|
||||||
|
|
||||||
|
@ -33,7 +35,36 @@ extension CardDeckView: View {
|
||||||
)
|
)
|
||||||
.stacked(at: index + 1, outOf: deckSize, offsetMultiple: CGFloat(30 / deckSize))
|
.stacked(at: index + 1, outOf: deckSize, offsetMultiple: CGFloat(30 / deckSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if self.differentiateWithoutColor {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
self.swipeDirectionIndicators
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(width: width, height: height)
|
.frame(width: width, height: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var swipeDirectionIndicators: some View {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "xmark.circle")
|
||||||
|
.padding()
|
||||||
|
.background(Color.black.opacity(0.7))
|
||||||
|
.clipShape(Circle())
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
|
||||||
|
Image(systemName: "checkmark.circle")
|
||||||
|
.padding()
|
||||||
|
.background(Color.black.opacity(0.7))
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.largeTitle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue