diff --git a/Workout Spinner WatchKit App/Info.plist b/Workout Spinner WatchKit App/Info.plist index 673fa3d..1520c64 100644 --- a/Workout Spinner WatchKit App/Info.plist +++ b/Workout Spinner WatchKit App/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 283 + 374 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/Workout Spinner WatchKit Extension/Extensions/Color-extension.swift b/Workout Spinner WatchKit Extension/Extensions/Color-extension.swift index 2592fde..8f2ff53 100644 --- a/Workout Spinner WatchKit Extension/Extensions/Color-extension.swift +++ b/Workout Spinner WatchKit Extension/Extensions/Color-extension.swift @@ -22,6 +22,33 @@ extension Color { } } +extension Color { + init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} + struct CustomColors_Previews: PreviewProvider { static var customColors: [Color] = [.workoutGreen, .darkGray, .deepRed, .deepRed2, .pastelDarkRed, .pastelDarkRed2] @@ -38,8 +65,10 @@ struct CustomColors_Previews: PreviewProvider { } extension Color { - static let workoutGreen = Color(red: 194, green: 255, blue: 60) - static let workoutRed = Color(red: 214, green: 26, blue: 82) + static let workoutGreen = Color(red: 175, green: 245, blue: 58) + static let darkWorkoutRed = Color(red: 227, green: 2, blue: 25) + static let workoutRed = Color(red: 237, green: 20, blue: 66) + static let lightWorkoutRed = Color(red: 249, green: 39, blue: 110) static let darkGray = Color(red: 40, green: 40, blue: 40) static let deepRed = Color(red: 232, green: 39, blue: 39) static let deepRed2 = Color(red: 200, green: 20, blue: 20) diff --git a/Workout Spinner WatchKit Extension/Info.plist b/Workout Spinner WatchKit Extension/Info.plist index 04fb8c7..e10e4e1 100644 --- a/Workout Spinner WatchKit Extension/Info.plist +++ b/Workout Spinner WatchKit Extension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 283 + 374 CLKComplicationPrincipalClass $(PRODUCT_MODULE_NAME).ComplicationController CLKComplicationSupportedFamilies diff --git a/Workout Spinner WatchKit Extension/Models/WorkoutTracker.swift b/Workout Spinner WatchKit Extension/Models/WorkoutTracker.swift index fe8f8d1..6a4b23f 100644 --- a/Workout Spinner WatchKit Extension/Models/WorkoutTracker.swift +++ b/Workout Spinner WatchKit Extension/Models/WorkoutTracker.swift @@ -19,12 +19,17 @@ class WorkoutTracker: NSObject, ObservableObject { /// Total active calories. var totalActiveCalories: Double { - return data.map { $0.activeCalories }.reduce(0, +) + data.map { $0.activeCalories }.reduce(0, +) } /// Total duration. var totalDuration: Double { - return data.map { $0.duration }.reduce(0, +) + data.map { $0.duration }.reduce(0, +) + } + + /// The total number of heart rate measurements. + var numberOfHeartRateMeasurements: Int { + data.map { $0.heartRate.count }.reduce(0, +) } /// Average heart rate reading. diff --git a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift index 7e5f627..f8bd539 100644 --- a/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift +++ b/Workout Spinner WatchKit Extension/Views/ExercisePicker.swift @@ -57,7 +57,9 @@ struct ExercisePicker: View { .animation(.default) HStack { - SpinnerPointer().frame(width: 20, height: 15) + SpinnerPointer() + .frame(width: 35, height: 25) + .opacity(0.8) Spacer() } } diff --git a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift index 1675a62..c77ce79 100644 --- a/Workout Spinner WatchKit Extension/Views/ExerciseView.swift +++ b/Workout Spinner WatchKit Extension/Views/ExerciseView.swift @@ -76,9 +76,9 @@ struct ExerciseView: View { Spacer() - Button(action: finishExercise, label: { - Text("Done").foregroundColor(.green).bold() - }) + DoneButton(action: finishExercise) + .padding(.horizontal, 8) + .padding(.top, 4) } } diff --git a/Workout Spinner WatchKit Extension/View Modifiers/ButtonStyles.swift b/Workout Spinner WatchKit Extension/Views/General Components/ButtonStyles.swift similarity index 82% rename from Workout Spinner WatchKit Extension/View Modifiers/ButtonStyles.swift rename to Workout Spinner WatchKit Extension/Views/General Components/ButtonStyles.swift index 2818406..702a5ad 100644 --- a/Workout Spinner WatchKit Extension/View Modifiers/ButtonStyles.swift +++ b/Workout Spinner WatchKit Extension/Views/General Components/ButtonStyles.swift @@ -10,12 +10,12 @@ import SwiftUI struct DoneButtonStyle: ButtonStyle { var padding: CGFloat = 5.0 - var cornerRadius: CGFloat = 5.0 - var color = Color.workoutGreen + var cornerRadius: CGFloat = 18.0 + var color = Color.workoutRed func makeBody(configuration: Configuration) -> some View { configuration.label - .padding(padding) + .padding(.vertical, padding) .background( RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) .fill(color) @@ -31,8 +31,10 @@ struct StartWorkoutButtonStyle: ButtonStyle { .padding(20) .background( RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(Color.darkGray) + .fill(Color.workoutRed) ) + .scaleEffect(configuration.isPressed ? 0.97 : 1.0) + .animation(.linear(duration: 0.2)) } } diff --git a/Workout Spinner WatchKit Extension/Views/General Components/Buttons.swift b/Workout Spinner WatchKit Extension/Views/General Components/Buttons.swift index fadac63..2f44fc3 100644 --- a/Workout Spinner WatchKit Extension/Views/General Components/Buttons.swift +++ b/Workout Spinner WatchKit Extension/Views/General Components/Buttons.swift @@ -8,23 +8,63 @@ import SwiftUI +struct CleanListViewButtonModifications: ViewModifier { + func body(content: Content) -> some View { + content + .listStyle(PlainListStyle()) + .listRowPlatterColor(.clear) + } +} + struct ListViewTextButton: View { - var label: String + var text: String var action: () -> Void var body: some View { Button(action: action) { - Text(label) + Text(text) .foregroundColor(.black) .frame(minWidth: 0, maxWidth: .infinity) } - .listStyle(PlainListStyle()) - .listRowPlatterColor(.clear) + .modifier(CleanListViewButtonModifications()) + } +} + +struct ListViewDoneButton: View { + var text: String = "Done" + var action: () -> Void + var verticalTextPadding: CGFloat = 3 + + var body: some View { + Button(action: action) { + Text(text) + .bold() + .padding(.vertical, verticalTextPadding) + .modifier(DoneButtonText()) + } + .modifier(CleanListViewButtonModifications()) + .buttonStyle(DoneButtonStyle()) + } +} + +struct DoneButton: View { + var text: String = "Done" + var action: () -> Void + var verticalTextPadding: CGFloat = 3 + + var body: some View { + Button(action: action) { + Text(text) + .bold() + .padding(.vertical, verticalTextPadding) + .modifier(DoneButtonText()) + } + .buttonStyle(DoneButtonStyle()) } } struct Buttons_Previews: PreviewProvider { static var previews: some View { - ListViewTextButton(label: "Button") {} + ListViewTextButton(text: "Button") {} } } diff --git a/Workout Spinner WatchKit Extension/View Modifiers/SpinnerRotationModifier.swift b/Workout Spinner WatchKit Extension/Views/General Components/SpinnerRotationModifier.swift similarity index 100% rename from Workout Spinner WatchKit Extension/View Modifiers/SpinnerRotationModifier.swift rename to Workout Spinner WatchKit Extension/Views/General Components/SpinnerRotationModifier.swift diff --git a/Workout Spinner WatchKit Extension/Views/General Components/Text ViewModifiers.swift b/Workout Spinner WatchKit Extension/Views/General Components/Text ViewModifiers.swift new file mode 100644 index 0000000..c83f34c --- /dev/null +++ b/Workout Spinner WatchKit Extension/Views/General Components/Text ViewModifiers.swift @@ -0,0 +1,18 @@ +// +// Text ViewModifiers.swift +// Workout Spinner WatchKit Extension +// +// Created by Joshua on 12/3/20. +// Copyright © 2020 Joshua Cook. All rights reserved. +// + +import SwiftUI + +struct DoneButtonText: ViewModifier { + func body(content: Content) -> some View { + content + .font(.system(.body, design: .rounded)) + .foregroundColor(.white) + .frame(minWidth: 0, maxWidth: .infinity) + } +} diff --git a/Workout Spinner WatchKit Extension/Views/HeartRateGraph/GraphBackgroundSegment.swift b/Workout Spinner WatchKit Extension/Views/HeartRateGraph/GraphBackgroundSegment.swift index a0d0a32..992ab66 100644 --- a/Workout Spinner WatchKit Extension/Views/HeartRateGraph/GraphBackgroundSegment.swift +++ b/Workout Spinner WatchKit Extension/Views/HeartRateGraph/GraphBackgroundSegment.swift @@ -44,7 +44,11 @@ struct GraphBackgroundSegment: View { /// - Parameter width: Width to be scaled. /// - Returns: Scaled width. func scaleWidth(_ width: Double) -> CGFloat { - CGFloat(width.rangeMap(inMin: 0, inMax: totalSegmentWidth, outMin: 0, outMax: Double(size.width))) + let x = CGFloat(width.rangeMap(inMin: 0, inMax: totalSegmentWidth, outMin: 0, outMax: Double(size.width))) + print("scaled width from \(width) to \(x)") + print(" totalSegmentWidth: \(totalSegmentWidth)") + print(" size.width: \(size.width)") + return x } /// Scale a value to the height of the entire frame. @@ -74,7 +78,7 @@ struct GraphBackgroundSegment: View { let maxX = xValues.max()! let minY = graphData.minY let maxY = graphData.maxY - data.append(RectangleSegment(width: maxX - minX, height: maxY - minY, color: colors[idx])) + data.append(RectangleSegment(width: max(1.0, maxX - minX), height: maxY - minY, color: colors[idx])) } return data diff --git a/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift b/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift index 4abfa44..4eca4b3 100644 --- a/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift +++ b/Workout Spinner WatchKit Extension/Views/Settings Views/EditExerciseView.swift @@ -126,9 +126,9 @@ struct EditExerciseView: View { } } - ListViewTextButton(label: "Save", action: saveAndFinish) - .buttonStyle(DoneButtonStyle(color: .workoutRed)) + ListViewDoneButton(text: "Save", action: saveAndFinish) .disabled(name.trimmingCharacters(in: .whitespacesAndNewlines) == "") + .opacity(name.trimmingCharacters(in: .whitespacesAndNewlines) == "" ? 0.7 : 1.0) } } } diff --git a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerPointer.swift b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerPointer.swift index 9731d8e..5e0b7cd 100644 --- a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerPointer.swift +++ b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerPointer.swift @@ -22,8 +22,8 @@ struct PointerTriangle: Shape { struct SpinnerPointer: View { var body: some View { PointerTriangle() - .foregroundColor(.white) - .shadow(color: .black, radius: 5, x: 5, y: 5) + .foregroundColor(.workoutRed) + .shadow(color: .black, radius: 5, x: 3, y: 3) } } diff --git a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift index dbc59f9..3a39ffe 100644 --- a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift +++ b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/SpinnerSlice.swift @@ -39,10 +39,12 @@ struct SpinnerSlice: View { var body: some View { ZStack { - Color.darkGray +// Color.darkGray +// LinearGradient(gradient: Gradient(colors: [.gray, .darkGray]), startPoint: .leading, endPoint: .trailing) + Color.white .clipShape(SpinnerSliceShape(radius: width / 2.0, angle: sliceAngle)) SpinnerSliceShape(radius: width / 2.0, angle: sliceAngle) - .stroke(Color.gray, lineWidth: 3) + .stroke(Color.black, lineWidth: 3) .frame(width: width, height: width) } .rotationEffect(rotationAngle) diff --git a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/WorkoutSlice.swift b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/WorkoutSlice.swift index efae44b..59b00f9 100644 --- a/Workout Spinner WatchKit Extension/Views/Spinner Subviews/WorkoutSlice.swift +++ b/Workout Spinner WatchKit Extension/Views/Spinner Subviews/WorkoutSlice.swift @@ -27,7 +27,7 @@ struct WorkoutSlice: View { Text(workoutInfo.displayName) .rotationEffect(.degrees(180)) .font(.system(size: 20)) - .foregroundColor(.white) + .foregroundColor(.black) .lineLimit(1) .padding(.horizontal, 5) .frame(width: size / 2 - offset, height: nil, alignment: .trailing) diff --git a/Workout Spinner WatchKit Extension/Views/WelcomeView.swift b/Workout Spinner WatchKit Extension/Views/WelcomeView.swift index f2a67fb..770099a 100644 --- a/Workout Spinner WatchKit Extension/Views/WelcomeView.swift +++ b/Workout Spinner WatchKit Extension/Views/WelcomeView.swift @@ -13,6 +13,8 @@ struct WelcomeView: View { @ObservedObject var workoutTracker: WorkoutTracker @ObservedObject var exerciseOptions: ExerciseOptions + @State var arrowButtonSize: CGFloat = 1.0 + @State private var startWorkout = false @State private var presentSettingsView = false @State private var showInstructions = false @@ -21,15 +23,25 @@ struct WelcomeView: View { VStack { Spacer(minLength: 0) + Text("Start Workout") + .font(.system(size: 25)) + .foregroundColor(.white) + .bold() + .multilineTextAlignment(.center) + .padding() + NavigationLink(destination: WorkoutPagingView(workoutManager: workoutManager, workoutTracker: workoutTracker, exerciseOptions: exerciseOptions)) { - Text("Start Workout") - .font(.system(size: 30)) + Image(systemName: "arrow.right.circle.fill") + .font(.system(size: 80)) .foregroundColor(.workoutRed) - .bold() - .multilineTextAlignment(.center) + .scaleEffect(arrowButtonSize) + .animation( + Animation.easeInOut(duration: 1.0) + .repeatForever(autoreverses: true) + ) } - .buttonStyle(StartWorkoutButtonStyle()) - .padding(.bottom, 5) + .buttonStyle(PlainButtonStyle()) + .padding(.vertical, 5) Spacer(minLength: 0) @@ -69,6 +81,7 @@ struct WelcomeView: View { }) } .onAppear { + self.arrowButtonSize = 0.9 workoutManager.requestAuthorization() workoutManager.setupWorkout() } diff --git a/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift b/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift index 48d400d..75bd0f8 100644 --- a/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift +++ b/Workout Spinner WatchKit Extension/Views/WorkoutFinishView.swift @@ -58,16 +58,20 @@ struct WorkoutFinishView: View { @Environment(\.presentationMode) var presentationMode + private var heartRateGraphIsAvailable: Bool { + workoutTracker.numberOfHeartRateMeasurements > 1 + } + private var averageHR: String { - return valueAsIntStringOrNA(workoutTracker.averageHeartRate) + valueAsIntStringOrNA(workoutTracker.averageHeartRate) } private var minHR: String { - return valueAsIntStringOrNA(workoutTracker.minHeartRate) + valueAsIntStringOrNA(workoutTracker.minHeartRate) } private var maxHR: String { - return valueAsIntStringOrNA(workoutTracker.maxHeartRate) + valueAsIntStringOrNA(workoutTracker.maxHeartRate) } @State private var showAllExercises: Bool = false @@ -92,8 +96,8 @@ struct WorkoutFinishView: View { InfoRowView(title: "Active Calories", titleColor: .yellow, value: "\(Int(workoutTracker.totalActiveCalories))") - LinkedInfoRowView(title: "Average heart rate", titleColor: .red, value: averageHR, showEllipsis: averageHR != "NA") { - if averageHR != "NA" { + LinkedInfoRowView(title: "Average heart rate", titleColor: .red, value: averageHR, showEllipsis: heartRateGraphIsAvailable) { + if heartRateGraphIsAvailable { showHeartRateChartView = true } } @@ -110,11 +114,10 @@ struct WorkoutFinishView: View { InfoRowView(title: "Min/Max heart rate", titleColor: .red, value: "\(minHR) / \(maxHR)") - ListViewTextButton(label: "Done") { + ListViewDoneButton { presentationMode.wrappedValue.dismiss() workoutTracker.clear() } - .buttonStyle(DoneButtonStyle(color: .workoutRed)) } } .navigationBarBackButtonHidden(true)