Added toolbar to the textfields in ContentView and you can now type zero without the app crashing
This commit is contained in:
parent
b6677c2053
commit
7aa899ca7d
6 changed files with 228 additions and 78 deletions
|
|
@ -23,7 +23,7 @@
|
|||
85AAA0D827FA2DD600F4B9A1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */; };
|
||||
85AAA0DA27FA2DDA00F4B9A1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85AAA0D927FA2DDA00F4B9A1 /* Assets.xcassets */; };
|
||||
85AAA0DD27FA2DDA00F4B9A1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85AAA0DC27FA2DDA00F4B9A1 /* Preview Assets.xcassets */; };
|
||||
85AAA0E627FA2EB100F4B9A1 /* ModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0E527FA2EB100F4B9A1 /* ModalView.swift */; };
|
||||
85AAA0E627FA2EB100F4B9A1 /* HelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0E527FA2EB100F4B9A1 /* HelpView.swift */; };
|
||||
85AAA0E827FA2F1600F4B9A1 /* ChangeLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AAA0E727FA2F1600F4B9A1 /* ChangeLogView.swift */; };
|
||||
85AAA0EB27FA2F7000F4B9A1 /* RichTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 85AAA0EA27FA2F7000F4B9A1 /* RichTextView */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
85AAA0D727FA2DD600F4B9A1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
85AAA0D927FA2DDA00F4B9A1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
85AAA0DC27FA2DDA00F4B9A1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
85AAA0E527FA2EB100F4B9A1 /* ModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalView.swift; sourceTree = "<group>"; };
|
||||
85AAA0E527FA2EB100F4B9A1 /* HelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpView.swift; sourceTree = "<group>"; };
|
||||
85AAA0E727FA2F1600F4B9A1 /* ChangeLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
850F80212853F7E30094580D /* SecretView.swift */,
|
||||
850F8015284A815C0094580D /* KnownIssues.swift */,
|
||||
85AAA0E727FA2F1600F4B9A1 /* ChangeLogView.swift */,
|
||||
85AAA0E527FA2EB100F4B9A1 /* ModalView.swift */,
|
||||
85AAA0E527FA2EB100F4B9A1 /* HelpView.swift */,
|
||||
850F801F2853F7790094580D /* LicenseView.swift */,
|
||||
85AAA0D927FA2DDA00F4B9A1 /* Assets.xcassets */,
|
||||
850F8025285437500094580D /* isaac.greene.clouds.heic */,
|
||||
|
|
@ -200,7 +200,7 @@
|
|||
850F8016284A815C0094580D /* KnownIssues.swift in Sources */,
|
||||
859298C928592F1F00D9D6CB /* Documentation.docc in Sources */,
|
||||
85AAA0E827FA2F1600F4B9A1 /* ChangeLogView.swift in Sources */,
|
||||
85AAA0E627FA2EB100F4B9A1 /* ModalView.swift in Sources */,
|
||||
85AAA0E627FA2EB100F4B9A1 /* HelpView.swift in Sources */,
|
||||
850F80202853F7790094580D /* LicenseView.swift in Sources */,
|
||||
850F8018284A83400094580D /* FeaturesView.swift in Sources */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,19 @@ import SwiftUI
|
|||
struct June2022: View {
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
//2022-06-15
|
||||
HStack {
|
||||
VStack (alignment: .leading) {
|
||||
Text("2022-06-15")
|
||||
.font(.title2)
|
||||
Text("Version Prerelease Build LVSXT10a.3\n")
|
||||
.font(.footnote)
|
||||
Text("\u{2022} Fixed an issue where typing zero for the distance caused the app to crash\n\u{2022} Implemented a way to navigate between text fields and redid the Done button to make it fit better")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(30)
|
||||
|
||||
//2022-06-14
|
||||
HStack {
|
||||
VStack (alignment: .leading) {
|
||||
|
|
@ -17,18 +30,19 @@ struct June2022: View {
|
|||
.font(.title2)
|
||||
Text("Version Prerelease Build LVSXT10a.3\n")
|
||||
.font(.footnote)
|
||||
Text("\u{2022} Optimized some code to allow for future development\n\u{2022} Starting on fixing an issue where putting a zero as the first number in the Distance field causes the app to crash\n\u{2022} Partially added more robust app documentation")
|
||||
Text("\u{2022} Optimized some code to allow for future development\n\u{2022} Starting on fixing an issue where putting a zero as the first number in the Distance field causes the app to crash\n\u{2022} Started work on more robust app documentation")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(30)
|
||||
|
||||
//2022-06-11
|
||||
// LVSXT10a.2
|
||||
HStack {
|
||||
VStack (alignment: .leading) {
|
||||
Text("2022-06-11")
|
||||
.font(.title2)
|
||||
Text("Version Release Candidate 2 (LVSXT10a.2)\n")
|
||||
Text("Version Release Candidate 2\n")
|
||||
.font(.footnote)
|
||||
Text("\u{2022} Added biometrics to sign in along with option for username/password")
|
||||
}
|
||||
|
|
@ -37,11 +51,12 @@ struct June2022: View {
|
|||
.padding(30)
|
||||
|
||||
//2022-06-10
|
||||
// LVSXT10a
|
||||
HStack {
|
||||
VStack (alignment: .leading) {
|
||||
Text("2022-06-10")
|
||||
.font(.title2)
|
||||
Text("Version Release Candidate (LVSXT10a)\n")
|
||||
Text("Version Release Candidate\n")
|
||||
.font(.footnote)
|
||||
Text("\u{2022} Got the conversion of paces to work, so a 5:00/km pace will also show as 8:03/mi\n\u{2022} This app is now considered \"Finished\" and will transition to Version Numbers\n\u{2022} FINALLY MADE A WAY TO DISMISS THE KEYBOARD\n\t\u{2022} (as with most of the other solutions I've had to use for other problems in this app, the fix was remarkably easy)\n\u{2022} Made several small quality-of-life improvements")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,15 @@ import Foundation
|
|||
///
|
||||
/// e
|
||||
struct ContentView: View {
|
||||
enum Field {
|
||||
case distance
|
||||
case hours
|
||||
case minutes
|
||||
case seconds
|
||||
}
|
||||
|
||||
@FocusState private var nameIsFocused: Bool
|
||||
@FocusState private var focusedField: Field?
|
||||
var SISystem = ["km","mi"]
|
||||
@State var timeHours: String = ""
|
||||
@State var timeMinutes: String = ""
|
||||
|
|
@ -21,66 +29,82 @@ struct ContentView: View {
|
|||
@State var distance: String = ""
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
NavigationView {
|
||||
VStack {
|
||||
TextField("Enter distance", text: $distance)
|
||||
.padding()
|
||||
.keyboardType(.decimalPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
}
|
||||
VStack {
|
||||
Text("Unit of measurement:")
|
||||
Picker("System of measurement", selection: $selectedSystem, content: {
|
||||
ForEach(SISystem, id: \.self, content: { unit in
|
||||
Text(unit)
|
||||
VStack {
|
||||
TextField("Enter distance", text: $distance)
|
||||
.padding()
|
||||
.keyboardType(.decimalPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
.focused($focusedField, equals: .distance)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .keyboard) {
|
||||
Button("Done") {
|
||||
nameIsFocused = false
|
||||
}
|
||||
Button("Next") {
|
||||
if focusedField == .distance {
|
||||
focusedField = .hours
|
||||
} else if focusedField == .hours {
|
||||
focusedField = .minutes
|
||||
} else if focusedField == .minutes {
|
||||
focusedField = .seconds
|
||||
} else {
|
||||
focusedField = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack {
|
||||
Text("Unit of measurement:")
|
||||
Picker("System of measurement", selection: $selectedSystem, content: {
|
||||
ForEach(SISystem, id: \.self, content: { unit in
|
||||
Text(unit)
|
||||
})
|
||||
})
|
||||
})
|
||||
.pickerStyle(.segmented)
|
||||
.frame(minWidth: 60, maxWidth: 300)
|
||||
HStack {
|
||||
VStack {
|
||||
Text("Hours")
|
||||
TextField("Enter hours", text: $timeHours)
|
||||
.keyboardType(.numberPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.trailing, -15)
|
||||
.padding()
|
||||
VStack {
|
||||
Text("Minutes")
|
||||
TextField("Enter minutes", text: $timeMinutes)
|
||||
.pickerStyle(.segmented)
|
||||
.frame(minWidth: 60, maxWidth: 300)
|
||||
HStack {
|
||||
VStack {
|
||||
Text("Hours")
|
||||
TextField("Enter hours", text: $timeHours)
|
||||
.keyboardType(.numberPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
.focused($focusedField, equals: .hours)
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.trailing, -15)
|
||||
.padding()
|
||||
VStack {
|
||||
Text("Minutes")
|
||||
TextField("Enter minutes", text: $timeMinutes)
|
||||
.keyboardType(.numberPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
.focused($focusedField, equals: .minutes)
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.trailing, -15)
|
||||
.padding(.leading, -15)
|
||||
.padding()
|
||||
VStack {
|
||||
Text("Seconds")
|
||||
TextField("Enter seconds", text: $timeSeconds)
|
||||
.keyboardType(.numberPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
.focused($focusedField, equals: .seconds)
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.leading, -15)
|
||||
.padding()
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.trailing, -15)
|
||||
.padding(.leading, -15)
|
||||
.padding()
|
||||
VStack {
|
||||
Text("Seconds")
|
||||
TextField("Enter seconds", text: $timeSeconds)
|
||||
.keyboardType(.numberPad)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.focused($nameIsFocused)
|
||||
}
|
||||
.frame(minWidth: 100)
|
||||
.padding(.leading, -15)
|
||||
.padding()
|
||||
}
|
||||
PaceResults(timeHours: $timeHours, timeMinutes: $timeMinutes, timeSeconds: $timeSeconds, selectedSystem: $selectedSystem, distance: $distance)
|
||||
}
|
||||
Spacer()
|
||||
if nameIsFocused == true {
|
||||
Button("Done") {
|
||||
nameIsFocused = false
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +117,7 @@ struct PaceResults: View {
|
|||
@Binding var distance: String
|
||||
|
||||
var body: some View {
|
||||
let distanceDub = Double(distance) ?? 1.0
|
||||
let distanceDub:Double = Double(removeLeadingZeros(distance: &distance)) ?? 1.0
|
||||
// because of some conversions I have to do,
|
||||
// this constant is a double just to make things easier.
|
||||
// this has to be one because the pace is calculated
|
||||
|
|
@ -106,7 +130,7 @@ struct PaceResults: View {
|
|||
let notSelectedSystem = (selectedSystem == "km" ? "mi" : "km")
|
||||
let convertedDistanceString:String = {
|
||||
let convertedDistance = distanceDub * multiplier
|
||||
return (distance == "" || distance.starts(with: "0") ? "0" : String(format: "%.3f", convertedDistance))
|
||||
return (distance == "" ? "0" : String(format: "%.3f", convertedDistance))
|
||||
}()
|
||||
|
||||
let convertedSeconds:Double = (Double(timeSeconds) ?? 0) * (1.6666666666666666666666666)
|
||||
|
|
@ -127,11 +151,11 @@ struct PaceResults: View {
|
|||
let timeMinutesInt:Int = (Int(timeMinutes) ?? 0) + (timeSecondsToMinutes)
|
||||
let timeMinutesUnderSixty:Int = timeMinutesInt % 60
|
||||
let timeMinutesToHours:Int = (timeMinutesInt - timeMinutesUnderSixty) / 60
|
||||
// this section tales the minutes (which it combines the
|
||||
// this section takes the minutes (which it combines the
|
||||
// minutes with the timeSecondsToMinutes) then finds out how
|
||||
// many hours (multiples of 60) are in the minutes value
|
||||
// and saves the hours in timeMinutesToHours and the remaining
|
||||
// minutes in timeMinutesUnderSixty
|
||||
// minutes in `timeMinutesUnderSixty`
|
||||
|
||||
let timeMinutesDouble:Double = Double(timeMinutes) ?? 0.0
|
||||
// this line of code takes the binding $timeMinutes which
|
||||
|
|
@ -141,13 +165,14 @@ struct PaceResults: View {
|
|||
// if the text field contains anything other than a number
|
||||
// or a single decimal, this value instantly becomes 0.0
|
||||
|
||||
let actualTime:Double = timeMinutesDouble + (convertedSeconds / 100) + ((Double(timeHours) ?? 0) * 60)
|
||||
// adds the minutes, hours, and seconds all together to get
|
||||
// a single value in terms of minutes, so that 1:08:45
|
||||
// becomes 68.75 which is a nice number we can do math on
|
||||
// and this number is never directly seen by the user
|
||||
|
||||
let pace = actualTime / distanceDub
|
||||
let pace:Double = {
|
||||
let actualTime:Double = timeMinutesDouble + (convertedSeconds / 100) + ((Double(timeHours) ?? 0) * 60)
|
||||
// adds the minutes, hours, and seconds all together to get
|
||||
// a single value in terms of minutes, so that 1:08:45
|
||||
// becomes 68.75 which is a nice number we can do math on
|
||||
// and this number is never directly seen by the user
|
||||
return actualTime / distanceDub
|
||||
}()
|
||||
|
||||
let paceFormatted:String = {
|
||||
let paceSeconds = pace.truncatingRemainder(dividingBy: 1.0)
|
||||
|
|
@ -168,12 +193,13 @@ struct PaceResults: View {
|
|||
return (paceOpposite >= 60 ? "\(paceHoursOpposite):\(properTimeMSOpposite)" : "\(properTimeMSOpposite)")
|
||||
}()
|
||||
|
||||
//let paceString:String = String(format: "%.2f", pace)
|
||||
let totalHours:Double = Double(timeMinutesToHours) + (Double(timeHours) ?? 0)
|
||||
// this takes the number of hours in the binding $timeHours
|
||||
// and the hours calculated in the previous section
|
||||
// and adds them together to get our total number of hours.
|
||||
let hoursFormatted:String = String(format: "%.0f", totalHours)
|
||||
let hoursFormatted:String = {
|
||||
let totalHours:Double = Double(timeMinutesToHours) + (Double(timeHours) ?? 0)
|
||||
// this takes the number of hours in the binding $timeHours
|
||||
// and the hours calculated in the previous section
|
||||
// and adds them together to get our total number of hours.
|
||||
return String(format: "%.0f", totalHours)
|
||||
}()
|
||||
|
||||
|
||||
let leadingZeros:String = String(format: "%02d:%02d", timeMinutesUnderSixty, timeSecondsUnderSixty)
|
||||
|
|
@ -198,6 +224,12 @@ struct PaceResults: View {
|
|||
.frame(minWidth: 100)
|
||||
}
|
||||
}
|
||||
func removeLeadingZeros(distance: inout String) -> String {
|
||||
while distance.starts(with: "0") {
|
||||
distance = distance.removing(prefix: "0")
|
||||
}
|
||||
return distance
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ struct DocsView: View {
|
|||
Section(header: Text("App Information")) {
|
||||
NavigationLink("Software License", destination: LicenseView())
|
||||
Text("Version: Prerelease Build LVSXT10a.3")
|
||||
Text("Release date: 2022-06-14")
|
||||
Text("Release date: 2022-06-15")
|
||||
Text("Start date: 2022-03-25")
|
||||
Link("Built with SwiftUI \(Image(systemName: "swift"))", destination: URL(string: "https://developer.apple.com/xcode/swiftui")!)
|
||||
}
|
||||
|
|
|
|||
103
Splits/HelpView.swift
Normal file
103
Splits/HelpView.swift
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// ModalView.swift
|
||||
// Splits
|
||||
//
|
||||
// Created by Isaac Greene on 4/3/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import RichTextView
|
||||
|
||||
struct HelpView: View {
|
||||
@State var mathSheet = false
|
||||
@State var problemSheet = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
Text("Help")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.top, 40)
|
||||
|
||||
Text("Due to limitations in the system, you can only use kilometers and miles at this time. \nSmaller units like meters and feet are not supported. \nYou can, however, use decimals, such as .2km or .8mi.\n\nI apologize for any inconvenience. In the future, I hope to make our app easier to use.\n\nI'm currently looking to add help articles about running to this page and it'll have loads of stuff about running and pace and all that stuff to actually help you with running. If I do, the purpose of this app might change.")
|
||||
.padding()
|
||||
|
||||
Button("See our math", action: {
|
||||
self.mathSheet.toggle()
|
||||
})
|
||||
.padding(30)
|
||||
.sheet(isPresented: self.$mathSheet, content: {
|
||||
algorithmView()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct algorithmView: View {
|
||||
var body: some View {
|
||||
Text("The Algorithm")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("Calculating pace is fairly straightforward, and does not change with increased complexity. The standard formula is simply this:\n")
|
||||
mathView()
|
||||
Text("\nWhere:\n")
|
||||
HStack {
|
||||
Text("\"t\"")
|
||||
.font(.custom("Charter", size: 18))
|
||||
Text("is total time")
|
||||
}
|
||||
HStack{
|
||||
Text("\"d\"")
|
||||
.font(.custom("Charter", size: 18))
|
||||
Text("is distance")
|
||||
}
|
||||
HStack {
|
||||
Text("\"p\"")
|
||||
.font(.custom("Charter", size: 18))
|
||||
Text("is the resulting pace")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct mathView: View {
|
||||
@State var mathString:String = "[math] \\frac{t}{d} &= p [/math]"
|
||||
|
||||
var body: some View {
|
||||
mathLaTeX_inator(mathString: $mathString)
|
||||
.padding(30)
|
||||
.frame(maxHeight: 300)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct mathLaTeX_inator: UIViewRepresentable {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Binding var mathString:String
|
||||
|
||||
func makeUIView(context: Context) -> RichTextView {
|
||||
let richTextView = RichTextView(
|
||||
input: mathString,
|
||||
latexParser: LatexParser(),
|
||||
font: UIFont.systemFont(ofSize: UIFont.systemFontSize),
|
||||
textColor: (colorScheme == .dark ? UIColor.white : UIColor.black),
|
||||
frame: CGRect.zero,
|
||||
completion: nil
|
||||
)
|
||||
return richTextView
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: RichTextView, context: Context) {
|
||||
uiView.update(
|
||||
input: mathString,
|
||||
latexParser: LatexParser(),
|
||||
font: UIFont.systemFont(ofSize: UIFont.systemFontSize),
|
||||
textColor: (colorScheme == .dark ? UIColor.white : UIColor.black),
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ struct RecentlyResolved: View {
|
|||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("\u{2022} Implemented an easy way to dismiss the keyboard in the main view of Calculator (it only took 2 1/2 months)\n\u{2022} Opening the contacts tab no longer causes the app to crash (RC 2)")
|
||||
Text("\u{2022} Implemented an easy way to dismiss the keyboard in the main view of Calculator (it only took 2 1/2 months)\n\u{2022} Opening the contacts tab no longer causes the app to crash (RC 2)\n\u{2022} Typing zero as the first number for the distance no longer causes the app to crash")
|
||||
}
|
||||
.padding(30)
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ struct HighPriority: View {
|
|||
var body: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
Text("\u{2022} Putting a zero as the first number in the Distance text field causes the app to crash\n\t\u{2022} **Workaround:** Just don't put zero first. You can still use decimals (like .7km and .24mi)")
|
||||
Text("\u{2022} Entering a distance of less than 0.1 per unit causes the app to crash\n\t**Workaround:** RIP to you if this applies")
|
||||
}
|
||||
.padding(30)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue