splits/Splits/ContentView.swift

196 lines
7.3 KiB
Swift

//
// ContentView.swift
// Splits
//
// Created by Isaac Greene on 4/3/22.
//
import SwiftUI
import Foundation
struct ContentView: View {
@FocusState private var nameIsFocused: Bool
var SISystem = ["km","mi"]
@State var timeHours: String = ""
@State var timeMinutes: String = ""
@State var timeSeconds: String = ""
@State var selectedSystem: String = "km"
@State var distance: String = ""
var body: some View {
VStack {
Spacer()
VStack {
TextField("Enter distance here", 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)
})
})
.pickerStyle(.segmented)
.frame(minWidth: 60, maxWidth: 300)
HStack {
VStack {
Text("Hours")
TextField("Enter hours here", text: $timeHours)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
.focused($nameIsFocused)
}
.frame(minWidth: 100)
.padding(.trailing, -15)
.padding()
VStack {
Text("Minutes")
TextField("Enter minutes here", text: $timeMinutes)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
.focused($nameIsFocused)
}
.frame(minWidth: 100)
.padding(.trailing, -15)
.padding(.leading, -15)
.padding()
VStack {
Text("Seconds")
TextField("Enter seconds here", 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()
}
}
}
}
struct PaceResults: View {
@Binding var timeHours: String
@Binding var timeMinutes: String
@Binding var timeSeconds: String
@Binding var selectedSystem: String
@Binding var distance: String
var body: some View {
let distanceDub = Double(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
// by time / distance, and you can't divide by 0.
// I could just make the TextField have a default value
// but then my message would disappear to let you know
// what to enter in that box
let multiplier = {selectedSystem == "mi" ? 1.609344 : 0.6213711922}()
let notSelectedSystem = {selectedSystem == "km" ? "mi" : "km"}()
let convertedDistance = distanceDub * multiplier
let convertedDistanceString = {distance == "" ? "" : String(format: "%.2f", convertedDistance)}()
let convertedSeconds:Double = (Double(timeSeconds) ?? 0) * (1.6666666666666666666666666)
let timeSecondsInt:Int = Int(timeSeconds) ?? 0
let timeSecondsUnderSixty:Int = (timeSecondsInt % 60)
// this section takes the seconds and multiplies it by 1.66
// so that 60 seconds becomes 100 (as in 100% of a minute)
// and this means that 30 seconds becomes 50 (as in 50% of a minute)
// which allows us to to calculate our pace. Without this
// the pace would be all wrong.
let timeSecondsToMinutes:Int = (timeSecondsInt - timeSecondsUnderSixty) / 60
// this takes the seconde and converts it to minutes so 78
// seconds will turn into 1 minute and 18 seconds leftover
// with the minutes saved in this value and the seconds
// disregarded because they're saved in timeSecondsUnderSixty
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
// 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
let timeMinutesDouble:Double = Double(timeMinutes) ?? 0.0
// this line of code takes the binding $timeMinutes which
// is a string and turns it into a number of type Double.
// while the TextField is a number pad, on iPads, Mac computers
// and using copy/paste you can get other characters.
// 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 paceOpposite = (selectedSystem == "km" ? (pace * 1.609344) : (pace * 0.6213711922))
let paceSeconds = pace.truncatingRemainder(dividingBy: 1.0)
let paceMinutes = (pace.truncatingRemainder(dividingBy: 60.0) - paceSeconds)
let paceHours = String(format: "%.0f", ((pace - paceMinutes) / 60))
let reConvertedSeconds = (paceSeconds / 1.666666666666666666) * 100
let properTimeMS = String(format: "%02d:%02d", Int(paceMinutes), Int(reConvertedSeconds.rounded()))
let paceSecondsOpposite = paceOpposite.truncatingRemainder(dividingBy: 1.0)
let paceMinutesOpposite = (paceOpposite.truncatingRemainder(dividingBy: 60.0) - paceSecondsOpposite)
let paceHoursOpposite = String(format: "%.0f", ((paceOpposite - paceMinutesOpposite) / 60))
let reConvertedSecondsOpposite = (paceSecondsOpposite / 1.666666666666666666) * 100
let properTimeMSOpposite = String(format: "%02d:%02d", Int(paceMinutesOpposite), Int(reConvertedSecondsOpposite.rounded()))
//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 paceFormatted:String = {pace >= 60 ? "\(paceHours):\(properTimeMS)" : "\(properTimeMS)"}()
let paceFormattedOpposite:String = {paceOpposite >= 60 ? "\(paceHoursOpposite):\(properTimeMSOpposite)" : "\(properTimeMSOpposite)"}()
let leadingZeros:String = String(format: "%02d:%02d", timeMinutesUnderSixty, timeSecondsUnderSixty)
// this takes the minutes and the seconds and adds leading
// zeros to them, so that 5 minutes 7 seconds will show
// as 05:07 which is a format most people, and certainly
// the people this app is intended for, will understand
HStack {
VStack {
Text("Distance: \(distance)\(selectedSystem)")
Text("Distance: \(convertedDistanceString)\(notSelectedSystem)")
}
Text("Total time\n\(hoursFormatted):\(leadingZeros)")
.padding()
VStack(alignment: .trailing) {
Text("\(paceFormatted) per \(selectedSystem)")
Text("\(paceFormattedOpposite) per \(notSelectedSystem)")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}