Apple Fitness workout fixer + Strava uploader

Add duration sort and use section header for title alignment

- Add segmented sort picker (Date/Longest/Shortest) below title
- Use Section header for "Overrun" title to align with row text
- Hide nav bar to reduce top spacing

+50 -4
+50 -4
WorkoutEditor/WorkoutListView.swift
··· 1 1 import SwiftUI 2 2 import HealthKit 3 3 4 + enum WorkoutSort: String, CaseIterable { 5 + case date = "Date" 6 + case longest = "Longest" 7 + case shortest = "Shortest" 8 + } 9 + 4 10 struct WorkoutListView: View { 5 11 var manager = HealthKitManager.shared 6 12 @Environment(\.scenePhase) private var scenePhase 7 13 @State private var searchText = "" 14 + @State private var sortMode: WorkoutSort = .date 8 15 9 16 private var filteredWorkouts: [HKWorkout] { 17 + var workouts: [HKWorkout] 10 18 if searchText.isEmpty { 11 - return manager.loadedWorkouts 19 + workouts = manager.loadedWorkouts 20 + } else { 21 + workouts = manager.loadedWorkouts.filter { 22 + $0.workoutActivityType.activityTypeDescription 23 + .localizedCaseInsensitiveContains(searchText) 24 + } 12 25 } 13 - return manager.loadedWorkouts.filter { 14 - $0.workoutActivityType.activityTypeDescription 15 - .localizedCaseInsensitiveContains(searchText) 26 + 27 + switch sortMode { 28 + case .date: 29 + return workouts 30 + case .longest: 31 + return workouts.sorted { $0.duration > $1.duration } 32 + case .shortest: 33 + return workouts.sorted { $0.duration < $1.duration } 16 34 } 17 35 } 18 36 19 37 private var groupedWorkouts: [(String, [HKWorkout])] { 38 + if sortMode != .date { 39 + return [("", filteredWorkouts)] 40 + } 41 + 20 42 let calendar = Calendar.current 21 43 let formatter = DateFormatter() 22 44 formatter.dateFormat = "MMMM yyyy" ··· 41 63 var body: some View { 42 64 NavigationStack { 43 65 List { 66 + Section { 67 + HStack { 68 + Text("Sort by") 69 + .font(.subheadline) 70 + .foregroundStyle(.secondary) 71 + Spacer() 72 + Picker("Sort", selection: $sortMode) { 73 + ForEach(WorkoutSort.allCases, id: \.self) { mode in 74 + Text(mode.rawValue).tag(mode) 75 + } 76 + } 77 + .pickerStyle(.segmented) 78 + .frame(width: 220) 79 + } 80 + } header: { 81 + Text("Overrun") 82 + .font(.largeTitle.bold()) 83 + .foregroundStyle(.primary) 84 + .textCase(nil) 85 + } 86 + 44 87 ForEach(groupedWorkouts, id: \.0) { section in 45 88 Section(section.0) { 46 89 ForEach(section.1, id: \.uuid) { workout in ··· 73 116 } 74 117 } 75 118 } 119 + .listSectionSpacing(.compact) 76 120 .searchable(text: $searchText, prompt: "Search by activity type") 77 121 .navigationDestination(for: HKWorkout.self) { workout in 78 122 WorkoutEditView(workout: workout) 79 123 } 80 124 .navigationTitle("Overrun") 125 + .navigationBarTitleDisplayMode(.inline) 126 + .toolbar(.hidden, for: .navigationBar) 81 127 .refreshable { 82 128 await manager.loadWorkouts() 83 129 }