Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created February 19, 2020 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriseidhof/8f043206036704d13ecc9f3736e5449e to your computer and use it in GitHub Desktop.
Save chriseidhof/8f043206036704d13ecc9f3736e5449e to your computer and use it in GitHub Desktop.
Race Pace Calculator
//
// ContentView.swift
// RunningPaceCalculator
//
// Created by Chris Eidhof on 19.02.20.
// Copyright © 2020 Chris Eidhof. All rights reserved.
//
import SwiftUI
struct Pace: Codable, Equatable, Hashable {
var secondsPerKM: Double
var secondsPerMile: Double {
return secondsPerKM * 1.60934
}
}
struct Meters: Codable, Equatable, Hashable {
var meters: Double
init(floatLiteral: Double) { self.meters = floatLiteral }
}
extension Meters: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral {
init(integerLiteral value: Double) {
meters = value
}
}
enum Distance {
case marathon
case halfMarathon
case other(Meters)
var displayName: String {
switch self {
case .marathon: return "Marathon"
case .halfMarathon: return "Half Marathon"
case .other(let m): return "\(Int(m.meters))m"
}
}
var distance: Meters {
switch self {
case .marathon: return 42195
case .halfMarathon: return 21097.5
case .other(let m): return m
}
}
static let builtin: [Distance] = [
.marathon,
.halfMarathon,
.other(10000),
.other(5000),
.other(1500),
.other(800),
.other(400),
.other(200)
]
}
struct Time: Codable, Equatable, Hashable {
var seconds: Double
}
let timeFormatter: DateComponentsFormatter = {
var t = DateComponentsFormatter()
t.allowedUnits = [.hour, .minute, .second]
t.unitsStyle = .positional
return t
}()
extension Time {
var formatted: String {
return timeFormatter.string(from: TimeInterval(seconds))!
}
}
func *(lhs: Pace, rhs: Meters) -> Time {
return Time(seconds: lhs.secondsPerKM * rhs.meters/1000)
}
func *(lhs: Pace, rhs: Double) -> Pace {
return Pace(secondsPerKM: lhs.secondsPerKM * rhs)
}
extension Pace {
var minutesPerKM: String {
let formatted = timeFormatter.string(from: TimeInterval(secondsPerKM))!
return "\(formatted)/km"
}
var minutesPerMile: String {
let formatted = timeFormatter.string(from: TimeInterval(secondsPerMile))!
return "\(formatted)/mi"
}
}
struct TimeSlider: View {
@Binding var pace: Pace
var body: some View {
HStack(alignment: .bottom, spacing: 5) {
ForEach(0..<(10*60)) { tick in
Rectangle()
.fill(Color.black)
.frame(width: 2, height: (tick % 10 == 0) ? 30 : 15)
}
}
}
}
struct ContentView: View {
@State var pace: Pace = Pace(secondsPerKM: 180+46)
var body: some View {
let separator = Rectangle()
.frame(height: 1)
.padding()
return VStack {
HStack {
Button("-") { self.pace.secondsPerKM -= 1 }
Text(pace.minutesPerKM)
Button("+") { self.pace.secondsPerKM += 1 }
}.font(Font.largeTitle.monospacedDigit())
Text(pace.minutesPerMile).font(Font.body.monospacedDigit())
Text("")
separator
ForEach(Array(Distance.builtin.enumerated()), id: \.offset) { (offset, el) in
HStack {
Text(el.displayName).frame(maxWidth: .infinity, alignment: .trailing)
Text((self.pace * el.distance).formatted).frame(maxWidth: .infinity, alignment: .leading)
}
}.font(Font.body.monospacedDigit())
separator
ForEach([10, 15, 20], id: \.self) { perc in
HStack {
Text("\(perc)% slower")
.frame(maxWidth: .infinity, alignment: .trailing)
Text((self.pace * (1 + Double(perc)/100)).minutesPerKM)
.frame(maxWidth: .infinity, alignment: .leading)
}
}.font(Font.body.monospacedDigit())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment