Open new workouts directly and improve accessibility
Starting a workout — from the split picker or a split's exercise list — now drops straight into its log screen once the cache catches up, via a shared StartedWorkoutNavigator. Adds VoiceOver labels/values to the log checkboxes and the settings button, a color-independent numbered legend and spoken summary to the heart-rate-zone bar, and Dynamic Type scaling to the run-flow badges and timer. Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
@@ -621,6 +621,10 @@ private struct PhaseTimerLayout<Content: View>: View {
|
||||
let tint: Color
|
||||
@ViewBuilder var timer: Content
|
||||
|
||||
/// Scales the big timer digits with Dynamic Type (falls back to `.minimumScaleFactor`
|
||||
/// so the largest sizes never overflow the top half).
|
||||
@ScaledMetric(relativeTo: .largeTitle) private var timerFontSize: CGFloat = 108
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 10) {
|
||||
Text(header)
|
||||
@@ -628,7 +632,7 @@ private struct PhaseTimerLayout<Content: View>: View {
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
timer
|
||||
.font(.system(size: 108, weight: .bold, design: .rounded))
|
||||
.font(.system(size: timerFontSize, weight: .bold, design: .rounded))
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(tint)
|
||||
.lineLimit(1)
|
||||
@@ -715,6 +719,9 @@ private extension View {
|
||||
private struct CompletedPhaseView: View {
|
||||
let log: WorkoutLogDocument?
|
||||
|
||||
/// Scales the completion badge with Dynamic Type.
|
||||
@ScaledMetric(relativeTo: .largeTitle) private var badgeSize: CGFloat = 96
|
||||
|
||||
/// "4 sets × 12 reps" / "3 sets × 45 sec".
|
||||
private var planSummary: String {
|
||||
guard let log else { return "" }
|
||||
@@ -736,7 +743,7 @@ private struct CompletedPhaseView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 14) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.system(size: 96, weight: .bold))
|
||||
.font(.system(size: badgeSize, weight: .bold))
|
||||
.foregroundStyle(Color.accentColor)
|
||||
Text("Completed")
|
||||
.font(.system(.title, design: .rounded, weight: .heavy))
|
||||
@@ -766,10 +773,13 @@ private struct CompletedPhaseView: View {
|
||||
/// ended early with this one unfinished). Same badge treatment as Completed,
|
||||
/// in gray, matching the list row's skipped icon.
|
||||
private struct SkippedPhaseView: View {
|
||||
/// Scales the skipped badge with Dynamic Type.
|
||||
@ScaledMetric(relativeTo: .largeTitle) private var badgeSize: CGFloat = 96
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 14) {
|
||||
Image(systemName: "wrongwaysign")
|
||||
.font(.system(size: 96, weight: .bold))
|
||||
.font(.system(size: badgeSize, weight: .bold))
|
||||
.foregroundStyle(.gray)
|
||||
Text("Skipped")
|
||||
.font(.system(.title, design: .rounded, weight: .heavy))
|
||||
@@ -785,10 +795,15 @@ private struct ReadyPhaseView: View {
|
||||
let summary: String
|
||||
let onStart: () -> Void
|
||||
|
||||
/// Scales the "Ready?" headline with Dynamic Type.
|
||||
@ScaledMetric(relativeTo: .largeTitle) private var readyFontSize: CGFloat = 44
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 14) {
|
||||
Text("Ready?")
|
||||
.font(.system(size: 44, weight: .bold, design: .rounded))
|
||||
.font(.system(size: readyFontSize, weight: .bold, design: .rounded))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.5)
|
||||
|
||||
if !summary.isEmpty {
|
||||
Text(summary)
|
||||
|
||||
Reference in New Issue
Block a user