From 76a6232d71b4c97ce735380298c2668a24befd21 Mon Sep 17 00:00:00 2001 From: Avery Pace Date: Sun, 7 Nov 2021 01:25:18 -0500 Subject: [PATCH] Cards are now in a stack --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 40 +---- .../ViewModels/FlashCardsViewModel.swift | 2 +- Toki Trainer/Views/ContentView.swift | 39 +++-- Toki Trainer/Views/FlashCardView.swift | 140 ++++++++++++------ Toki Trainer/Views/Toki_TrainerApp.swift | 6 +- 5 files changed, 125 insertions(+), 102 deletions(-) diff --git a/Toki Trainer.xcodeproj/xcuserdata/averyadapace.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Toki Trainer.xcodeproj/xcuserdata/averyadapace.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index edef04e..edc97ec 100644 --- a/Toki Trainer.xcodeproj/xcuserdata/averyadapace.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Toki Trainer.xcodeproj/xcuserdata/averyadapace.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -4,38 +4,6 @@ type = "1" version = "2.0"> - - - - - - - - + startingLineNumber = "194" + endingLineNumber = "194" + landmarkName = "CardFlipModifier" + landmarkType = "14"> diff --git a/Toki Trainer/ViewModels/FlashCardsViewModel.swift b/Toki Trainer/ViewModels/FlashCardsViewModel.swift index 3c93748..6cee3ed 100644 --- a/Toki Trainer/ViewModels/FlashCardsViewModel.swift +++ b/Toki Trainer/ViewModels/FlashCardsViewModel.swift @@ -18,7 +18,7 @@ class FlashCardsViewModel: ObservableObject { if let safeDictionary = jsonLoader.loadDictionary() { fullDictionary = safeDictionary randomDictionary = safeDictionary - //randomDictionary.shuffle() + randomDictionary.shuffle() } } diff --git a/Toki Trainer/Views/ContentView.swift b/Toki Trainer/Views/ContentView.swift index cee7d32..bc5ceba 100644 --- a/Toki Trainer/Views/ContentView.swift +++ b/Toki Trainer/Views/ContentView.swift @@ -15,10 +15,33 @@ extension String: Identifiable { struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext + @State private var tokiInput: String = "" + + var body: some View { + TabView { + TranslatorView() + .tabItem { + Image(systemName: "pencil") + Text("Phrase Lookup") + } + FlashCardView() + .tabItem { + Image(systemName: "character.textbox") + Text("Flash Cards") + } + } + } + + func openPartsOfSpeechView() { + print("Button pressed.") + } +} + +struct TranslatorView: View { @ObservedObject var tokiDictViewModel = TokiDictionaryViewModel() @State private var selectedPartOfSpeech: String? @State private var tokiInput: String = "" - + var body: some View { VStack { TextField("Enter Toki Pona Word or Phrase", text: $tokiInput) @@ -53,25 +76,11 @@ struct ContentView: View { } } } - .safeAreaInset(edge: .bottom) { - HStack() { - Button("Parts of Speech") { - self.selectedPartOfSpeech = "" - } - .padding(8) - } - .frame(maxWidth: .infinity) - .background(.thinMaterial) - } .sheet(item: $selectedPartOfSpeech) { selectedPOS in PartsOfSpeechView(selectedPartOfSpeech: selectedPOS, partsOfSpeech: tokiDictViewModel.partsOfSpeech) } } } - - func openPartsOfSpeechView() { - print("Button pressed.") - } } diff --git a/Toki Trainer/Views/FlashCardView.swift b/Toki Trainer/Views/FlashCardView.swift index 7af0bea..12b2bb1 100644 --- a/Toki Trainer/Views/FlashCardView.swift +++ b/Toki Trainer/Views/FlashCardView.swift @@ -7,32 +7,46 @@ import SwiftUI +enum FlashCardResult { + case Correct + case Incorrect + case Unanswered +} + struct FlashCardView: View { - //@ObservedObject var tokiDictViewModel = TokiDictionaryViewModel() @ObservedObject var flashCardsViewModel = FlashCardsViewModel() var body: some View { VStack { - //FlashCard(canBeFlipped: true, dictionaryEntry: tokiDictViewModel.dictionary.first!) FlashCardStack(dictionary: flashCardsViewModel.randomDictionary) } } } +extension Binding { + func onChange(_ handler: @escaping () -> ()) -> Binding { + Binding( + get: { self.wrappedValue }, + set: { newValue in + self.wrappedValue = newValue + handler() + }) + } +} + struct FlashCardStack: View { var dictionary: [TokiDictEntry] @State private var flashCards: [FlashCard] = [] @State private var topFlashCard: FlashCard? = nil @State private var flashCardStack: [FlashCard] = [] - @State private var flashCardsCanBeFlipped: [Bool] = [] + @State private var flashCardsAreInteractive: [Bool] = [] @State private var flashCardsVertOffset: [CGFloat] = [] + @State private var flashCardsResults: [FlashCardResult] = [] @State private var currentFlashCard = 0 - let timer = Timer.publish(every: 1, tolerance: 0.1, on: .main, in: .common, options: nil).autoconnect() - var body: some View { VStack { ZStack { @@ -40,55 +54,66 @@ struct FlashCardStack: View { ForEach(flashCards.indices, id: \.self) { index in flashCards[index] .offset(x: 0, y: flashCardsVertOffset[index]) + .zIndex(-(CGFloat(index * 100))) } } - // if(flashCards.count > 1) { -// flashCards[currentFlashCard] -// .offset(x: 0, y: flashCardsVertOffset[currentFlashCard]) -// .animation(.default) -// flashCards[currentFlashCard + 1] -// .offset(x: 0, y: flashCardsVertOffset[currentFlashCard + 1]) -// .animation(.default) -// } } - Spacer() - Button { - //self.currentFlashCard += 1 - nextFlashCard() - } label: { - Text("Next Card") - } - .background(.white) } + Spacer() .onAppear { - initFlashCardsArray() + initFlashCards() + print(currentFlashCard) } } - func initFlashCardsArray() { + func initFlashCards() { flashCards = [] for index in dictionary.indices { - flashCardsCanBeFlipped.append(false) - flashCards.append(FlashCard(canBeFlipped: $flashCardsCanBeFlipped[index], dictionaryEntry: dictionary[index])) - flashCardsVertOffset.append(800) + flashCardsAreInteractive.append(false) + flashCardsResults.append(FlashCardResult.Unanswered) + flashCards.append(FlashCard(isInteractive: $flashCardsAreInteractive[index], result: $flashCardsResults[index].onChange(nextFlashCard), dictionaryEntry: dictionary[index])) + flashCardsVertOffset.append(470) } + if flashCards.count - currentFlashCard >= 3 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + flashCardsVertOffset[currentFlashCard + 2] = 440 + flashCardsVertOffset[currentFlashCard + 3] = 470 + } else if flashCards.count - currentFlashCard == 2 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + flashCardsVertOffset[currentFlashCard + 2] = 440 + } else if flashCards.count - currentFlashCard == 1 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + } + + flashCardsVertOffset[currentFlashCard] = 100 + flashCardsAreInteractive[currentFlashCard] = true } func nextFlashCard() { + currentFlashCard += 1 if(currentFlashCard > 0 ) { flashCardsVertOffset[currentFlashCard - 1] = -1000 } - flashCardsVertOffset[currentFlashCard] = 300 - flashCards[currentFlashCard].setCanBeFlipped(true) - currentFlashCard += 1 - //flashCardsVertOffset[currentFlashCard + 1] = 300 + flashCardsVertOffset[currentFlashCard] = 100 + flashCardsAreInteractive[currentFlashCard] = true + + if flashCards.count - currentFlashCard >= 3 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + flashCardsVertOffset[currentFlashCard + 2] = 440 + flashCardsVertOffset[currentFlashCard + 3] = 470 + } else if flashCards.count - currentFlashCard == 2 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + flashCardsVertOffset[currentFlashCard + 2] = 440 + } else if flashCards.count - currentFlashCard == 1 { + flashCardsVertOffset[currentFlashCard + 1] = 410 + } } func setTopFlashCard(card: FlashCard?) { if let safeCard = card { - self.topFlashCard?.canBeFlipped = false + self.topFlashCard?.isInteractive = false self.topFlashCard = safeCard - self.topFlashCard?.canBeFlipped = true + self.topFlashCard?.isInteractive = true } } } @@ -98,40 +123,64 @@ struct FlashCard: View { @State var isFaceDown = false @State var rotationAngle: Double = 0 - @Binding var canBeFlipped: Bool + @Binding var isInteractive: Bool + @Binding var result: FlashCardResult var dictionaryEntry: TokiDictEntry + @State private var dragAmount = CGFloat(0) + + var drag: some Gesture { + DragGesture() + .onChanged { gesture in + if isInteractive { + self.dragAmount = gesture.translation.width + } + } + .onEnded { gesture in + withAnimation { + if isInteractive { + if self.dragAmount < -20 { + self.dragAmount = -500 + self.result = FlashCardResult.Incorrect + } else if self.dragAmount > 20 { + self.dragAmount = 500 + self.result = FlashCardResult.Correct + } + } + } + } + } + var body: some View { - let flipDegrees = isFaceDown ? 0.0 : 180.0 - Text("") .modifier(CardFlipModifier(isFaceDown: isFaceDown, frontText: dictionaryEntry.word, backText: concatenateDefinitions())) .frame(width: 0.8 * screen.width, height: 200.0) + .offset(x: isFaceDown ? -dragAmount : dragAmount, y: abs(dragAmount) / 10) + .rotationEffect(.degrees(isFaceDown ? -(dragAmount / 50) : dragAmount / 50)) .font(.title) .rotation3DEffect(self.isFaceDown ? Angle(degrees: 180) : Angle(degrees: 0), axis: (x: 0.0, y: 10.0, z: 0.0)) .animation(.default) .onTapGesture { - print("onTapGesture called") - if self.canBeFlipped == true { + if self.isInteractive == true { self.isFaceDown.toggle() } } + .gesture(drag) } func concatenateDefinitions() -> String { var result = String() for definition in dictionaryEntry.definitions { - result.append(contentsOf: definition.definition) + result.append(contentsOf: "\(definition.definition)\n") } return result } func setCanBeFlipped(_ input: Bool) { - print("setCanBeFlipped called") - self.canBeFlipped.toggle() + self.isInteractive.toggle() } } @@ -158,26 +207,23 @@ struct CardFlipModifier: AnimatableModifier { return ZStack { RoundedRectangle(cornerRadius: 20.0) .fill(rotationAngle < 90 ? Color.blue : Color.cyan) - .animation(.none) + .animation(.none, value: rotationAngle) .overlay( RoundedRectangle(cornerRadius: 20) .stroke(.cyan, lineWidth: 5)) + .animation(.none, value: rotationAngle) Text(frontText) .font(.title) .opacity(rotationAngle < 90 ? 1.0 : 0.0) - .animation(.none) + .animation(.none, value: rotationAngle) Text(backText) .font(.subheadline) .padding() .opacity(rotationAngle < 90 ? 0.0 : 1.0) + .animation(.none, value: rotationAngle) .scaleEffect(CGSize(width: -1.0, height: 1.0)) - .animation(.none) } } - - private func getCardColor() -> Color { - rotationAngle < 90 ? Color.blue : Color.cyan - } } struct FlashCardView_Previews: PreviewProvider { diff --git a/Toki Trainer/Views/Toki_TrainerApp.swift b/Toki Trainer/Views/Toki_TrainerApp.swift index e9e369f..fac8ce2 100644 --- a/Toki Trainer/Views/Toki_TrainerApp.swift +++ b/Toki Trainer/Views/Toki_TrainerApp.swift @@ -13,9 +13,9 @@ struct Toki_TrainerApp: App { var body: some Scene { WindowGroup { - FlashCardView() - //ContentView() - // .environment(\.managedObjectContext, persistenceController.container.viewContext) + //FlashCardView() + ContentView() + .environment(\.managedObjectContext, persistenceController.container.viewContext) } } }