From d5e3d292258f23c0c900df9bdc5f65a1b855e81a Mon Sep 17 00:00:00 2001 From: Avery Pace Date: Thu, 4 Nov 2021 14:10:22 -0400 Subject: [PATCH] Part of speech highlighting when tapped from dictionary list --- Toki Trainer.xcodeproj/project.pbxproj | 18 ++++--- Toki Trainer/Models/TokiJSONLoader.swift | 14 +++--- .../States/TokiPartsOfSpeechProvider.swift | 8 --- .../ViewModels/TokiDictionaryViewModel.swift | 25 ++++++++++ Toki Trainer/Views/ContentView.swift | 25 +++++++--- Toki Trainer/Views/PartsOfSpeechView.swift | 49 +++++++++++++++++++ 6 files changed, 108 insertions(+), 31 deletions(-) delete mode 100644 Toki Trainer/States/TokiPartsOfSpeechProvider.swift create mode 100644 Toki Trainer/ViewModels/TokiDictionaryViewModel.swift create mode 100644 Toki Trainer/Views/PartsOfSpeechView.swift diff --git a/Toki Trainer.xcodeproj/project.pbxproj b/Toki Trainer.xcodeproj/project.pbxproj index 5f47640..110dad7 100644 --- a/Toki Trainer.xcodeproj/project.pbxproj +++ b/Toki Trainer.xcodeproj/project.pbxproj @@ -7,12 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 7E20D5FF2733AFE700D75B9A /* PartsOfSpeechView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E20D5FE2733AFE700D75B9A /* PartsOfSpeechView.swift */; }; + 7E20D6012734466800D75B9A /* TokiDictionaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E20D6002734466800D75B9A /* TokiDictionaryViewModel.swift */; }; 7E2811172733027F0063DC78 /* TokiDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2811142733027F0063DC78 /* TokiDictionary.swift */; }; 7E2811182733027F0063DC78 /* TokiJSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2811152733027F0063DC78 /* TokiJSONLoader.swift */; }; 7E2811192733027F0063DC78 /* TokiPartOfSpeech.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2811162733027F0063DC78 /* TokiPartOfSpeech.swift */; }; 7E28111C273302860063DC78 /* toki-partsofspeech.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111A273302860063DC78 /* toki-partsofspeech.json */; }; 7E28111D273302860063DC78 /* toki-dictionary.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111B273302860063DC78 /* toki-dictionary.json */; }; - 7E281120273303220063DC78 /* TokiPartsOfSpeechProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E28111F273303220063DC78 /* TokiPartsOfSpeechProvider.swift */; }; 7E28112227330DD30063DC78 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E28112127330DD20063DC78 /* Constants.swift */; }; 7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E943A20273211C200E7DDF4 /* Toki_TrainerApp.swift */; }; 7E943A23273211C200E7DDF4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E943A22273211C200E7DDF4 /* ContentView.swift */; }; @@ -23,12 +24,13 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 7E20D5FE2733AFE700D75B9A /* PartsOfSpeechView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartsOfSpeechView.swift; sourceTree = ""; }; + 7E20D6002734466800D75B9A /* TokiDictionaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokiDictionaryViewModel.swift; sourceTree = ""; }; 7E2811142733027F0063DC78 /* TokiDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokiDictionary.swift; sourceTree = ""; }; 7E2811152733027F0063DC78 /* TokiJSONLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokiJSONLoader.swift; sourceTree = ""; }; 7E2811162733027F0063DC78 /* TokiPartOfSpeech.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokiPartOfSpeech.swift; sourceTree = ""; }; 7E28111A273302860063DC78 /* toki-partsofspeech.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-partsofspeech.json"; sourceTree = ""; }; 7E28111B273302860063DC78 /* toki-dictionary.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-dictionary.json"; sourceTree = ""; }; - 7E28111F273303220063DC78 /* TokiPartsOfSpeechProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokiPartsOfSpeechProvider.swift; sourceTree = ""; }; 7E28112127330DD20063DC78 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 7E943A1D273211C200E7DDF4 /* Toki Trainer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Toki Trainer.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 7E943A20273211C200E7DDF4 /* Toki_TrainerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toki_TrainerApp.swift; sourceTree = ""; }; @@ -65,16 +67,17 @@ children = ( 7E943A20273211C200E7DDF4 /* Toki_TrainerApp.swift */, 7E943A22273211C200E7DDF4 /* ContentView.swift */, + 7E20D5FE2733AFE700D75B9A /* PartsOfSpeechView.swift */, ); path = Views; sourceTree = ""; }; - 7E281113273302530063DC78 /* States */ = { + 7E281113273302530063DC78 /* ViewModels */ = { isa = PBXGroup; children = ( - 7E28111F273303220063DC78 /* TokiPartsOfSpeechProvider.swift */, + 7E20D6002734466800D75B9A /* TokiDictionaryViewModel.swift */, ); - path = States; + path = ViewModels; sourceTree = ""; }; 7E28111E273302890063DC78 /* JSON Data */ = { @@ -106,7 +109,7 @@ isa = PBXGroup; children = ( 7E28111E273302890063DC78 /* JSON Data */, - 7E281113273302530063DC78 /* States */, + 7E281113273302530063DC78 /* ViewModels */, 7E2811122733024F0063DC78 /* Views */, 7E281111273302420063DC78 /* Models */, 7E943A24273211C300E7DDF4 /* Assets.xcassets */, @@ -200,8 +203,9 @@ files = ( 7E943A2D273211C300E7DDF4 /* Toki_Trainer.xcdatamodeld in Sources */, 7E943A2A273211C300E7DDF4 /* Persistence.swift in Sources */, + 7E20D5FF2733AFE700D75B9A /* PartsOfSpeechView.swift in Sources */, + 7E20D6012734466800D75B9A /* TokiDictionaryViewModel.swift in Sources */, 7E2811192733027F0063DC78 /* TokiPartOfSpeech.swift in Sources */, - 7E281120273303220063DC78 /* TokiPartsOfSpeechProvider.swift in Sources */, 7E943A23273211C200E7DDF4 /* ContentView.swift in Sources */, 7E2811172733027F0063DC78 /* TokiDictionary.swift in Sources */, 7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */, diff --git a/Toki Trainer/Models/TokiJSONLoader.swift b/Toki Trainer/Models/TokiJSONLoader.swift index 3013d1b..5b8208c 100644 --- a/Toki Trainer/Models/TokiJSONLoader.swift +++ b/Toki Trainer/Models/TokiJSONLoader.swift @@ -8,28 +8,26 @@ import Foundation class TokiJSONLoader: ObservableObject { - @Published var dictionary: [TokiDictEntry] = [] - @Published var partsOfSpeech: [TokiPartOfSpeech] = [] - func loadDictionary() { + func loadDictionary() -> [TokiDictEntry]? { let jsonData = loadJSON("toki-dictionary") do { let decodedData = try JSONDecoder().decode([TokiDictEntry].self, from: jsonData!) - dictionary = decodedData + return decodedData } catch { print("Decode error: \(error)") - return + return nil } } - func loadPOS() { + func loadPOS() -> [TokiPartOfSpeech]? { let jsonData = loadJSON("toki-partsofspeech") do { let decodedData = try JSONDecoder().decode([TokiPartOfSpeech].self, from: jsonData!) - partsOfSpeech = decodedData + return decodedData } catch { print("Decode error: \(error)") - return + return nil } } diff --git a/Toki Trainer/States/TokiPartsOfSpeechProvider.swift b/Toki Trainer/States/TokiPartsOfSpeechProvider.swift deleted file mode 100644 index b80c043..0000000 --- a/Toki Trainer/States/TokiPartsOfSpeechProvider.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// TokiPartsOfSpeechProvider.swift -// Toki Trainer -// -// Created by Avery Ada Pace on 11/3/21. -// - -import Foundation diff --git a/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift b/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift new file mode 100644 index 0000000..f3fcb4c --- /dev/null +++ b/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift @@ -0,0 +1,25 @@ +// +// TokiDictionaryViewModel.swift +// Toki Trainer +// +// Created by Avery Ada Pace on 11/4/21. +// + +import Foundation + +class TokiDictionaryViewModel: ObservableObject +{ + let jsonLoader: TokiJSONLoader = TokiJSONLoader() + + @Published var dictionary: [TokiDictEntry] = [] + @Published var partsOfSpeech: [TokiPartOfSpeech] = [] + + init() { + if let safeDictionary = jsonLoader.loadDictionary() { + dictionary = safeDictionary + } + if let safePartsOfSpeech = jsonLoader.loadPOS() { + partsOfSpeech = safePartsOfSpeech + } + } +} diff --git a/Toki Trainer/Views/ContentView.swift b/Toki Trainer/Views/ContentView.swift index d8fdd70..7a20546 100644 --- a/Toki Trainer/Views/ContentView.swift +++ b/Toki Trainer/Views/ContentView.swift @@ -8,22 +8,30 @@ import SwiftUI import CoreData +extension String: Identifiable { + public var id: String { self } +} + struct ContentView: View { + @Environment(\.managedObjectContext) private var viewContext - @ObservedObject var jsonLoader = TokiJSONLoader() + @ObservedObject var tokiDictViewModel = TokiDictionaryViewModel() + @State private var selectedPartOfSpeech: String? = nil var body: some View { VStack { TextField("Enter Toki Pona Word or Phrase", text: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Value@*/.constant("")/*@END_MENU_TOKEN@*/) .multilineTextAlignment(.center) .padding(8) - List(jsonLoader.dictionary, id: \.word) { entry in + List(tokiDictViewModel.dictionary, id: \.word) { entry in VStack(alignment: .leading) { Text(entry.word) .font(.title) ForEach(entry.definitions, id: \.pos) { definition in HStack(alignment: .top) { - Button(action: openPartsOfSpeechView) { + Button(action: { + self.selectedPartOfSpeech = String(definition.pos) + }) { Text(definition.pos) .frame(width: 45, height: 22, alignment: .center) .foregroundColor(.black) @@ -31,6 +39,7 @@ struct ContentView: View { .cornerRadius(5.0) .padding(4) } + .buttonStyle(BorderlessButtonStyle()) Text(definition.definition) .fixedSize(horizontal: false, vertical: true) .padding(4) @@ -40,19 +49,19 @@ struct ContentView: View { } .safeAreaInset(edge: .bottom) { HStack() { - Button(action: openPartsOfSpeechView) { - Text("Parts of Speech") + Button("Parts of Speech") { + self.selectedPartOfSpeech = "" } .padding(8) } .frame(maxWidth: .infinity) .background(.thinMaterial) } - .onAppear { - self.jsonLoader.loadDictionary() + .sheet(item: $selectedPartOfSpeech) { selectedPOS in + PartsOfSpeechView(selectedPartOfSpeech: selectedPOS, tokiDictViewModel: self.tokiDictViewModel) } } -} + } func openPartsOfSpeechView() { print("Button pressed.") diff --git a/Toki Trainer/Views/PartsOfSpeechView.swift b/Toki Trainer/Views/PartsOfSpeechView.swift new file mode 100644 index 0000000..5046972 --- /dev/null +++ b/Toki Trainer/Views/PartsOfSpeechView.swift @@ -0,0 +1,49 @@ +// +// PartsOfSpeechView.swift +// Toki Trainer +// +// Created by Avery Ada Pace on 11/4/21. +// + +import SwiftUI + +struct PartsOfSpeechView: View { + var selectedPartOfSpeech: String? = nil + + @ObservedObject var tokiDictViewModel: TokiDictionaryViewModel + +// init(selectedPartOfSpeech: String) { +// _selectedPartOfSpeech = State(initialValue: selectedPartOfSpeech) +// } + + var body: some View { + VStack { + Text("Parts of Speech") + .padding() + VStack(alignment: .leading) { + ForEach(tokiDictViewModel.partsOfSpeech, id: \.pos) { pos in + HStack { + Text(pos.pos) + .frame(width: 45, height: 22, alignment: .center) + .background(Color(K.posColors[pos.pos]!)) + .cornerRadius(5.0) + .padding(1) + Text(pos.definition) + Spacer() + } + //.background(.blue) + .background((selectedPartOfSpeech == pos.pos) ? Color(UIColor.systemGray4) : .white) + .cornerRadius(5.0) + .padding(2) + } + } + Spacer() + } + } +} + +struct PartsOfSpeechView_Previews: PreviewProvider { + static var previews: some View { + PartsOfSpeechView(selectedPartOfSpeech: "sep", tokiDictViewModel: TokiDictionaryViewModel()) + } +}