Watch : cible clinique, couleurs alignées sur l'iPhone, aperçus Xcode
- `HeartRateZones` porte `clinicalTargetBpm` et `isUseful(bpm:)`. Sous bêtabloquant, le repère utile n'est PAS le bas de Z4 mais HRKarv0,60 (Díaz-Buschmann 2014, ESC : la Karvonen standard sous-évalue le seuil chez le patient traité). Un sceau vert l'indique sous la plage, et VoiceOver le dit — une icône seule ne suffit pas. - Z1 passe de `.blue` à `.cyan` : `CoachLiveView` (iPhone) utilise déjà cyan. Le même effort doit avoir la même teinte sur les deux écrans de la même app ; j'avais introduit la divergence en écrivant le modèle sans regarder l'existant. - Deux `#Preview` avec les zones RÉELLES de production (Z1 100-109 … Z5 134-143) : les cinq états d'un coup, plus le cas « zones inconnues ». 109 bpm y figure exprès — c'est le plancher de Z2, il vérifie la règle du chevauchement d'un battement. ⚠️ `CoachLiveView.CardiacZones` reste une SECONDE implémentation de la même règle, avec sa propre table de zones. Non fusionnée ici pour ne pas mélanger deux chantiers ; c'est le même motif que les deux traductions WorkoutKit divergentes corrigées ce matin. ⚠️ NON COMPILÉ — build Xcode requis.
This commit is contained in:
@@ -202,15 +202,31 @@ private struct HeartRateMetric: View {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
if let zone, let bounds = zones?[zone], bounds.count == 2 {
|
||||
Text("\(zone.name) · \(bounds[0])–\(bounds[1])")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
HStack(spacing: 4) {
|
||||
Text("\(zone.name) · \(bounds[0])–\(bounds[1])")
|
||||
// Sous bêtabloquant, le repère qui compte n'est pas le bas
|
||||
// de Z4 mais HRKarv0,60 (Díaz-Buschmann 2014, ESC) : la
|
||||
// Karvonen standard sous-évalue le seuil chez le patient
|
||||
// traité. L'écran iPhone l'affiche déjà.
|
||||
if zones?.isUseful(bpm: bpm) == true {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.foregroundStyle(.green)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("Fréquence cardiaque")
|
||||
.accessibilityValue(zone.map { "\(bpm) battements par minute, zone \($0.label), \($0.name)" }
|
||||
?? "\(bpm) battements par minute")
|
||||
.accessibilityValue(accessibilityDescription)
|
||||
}
|
||||
|
||||
private var accessibilityDescription: String {
|
||||
guard let zone else { return "\(bpm) battements par minute" }
|
||||
let useful = zones?.isUseful(bpm: bpm) == true ? ", zone d'entraînement utile" : ""
|
||||
return "\(bpm) battements par minute, zone \(zone.label), \(zone.name)\(useful)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,3 +260,32 @@ private struct Metric: View {
|
||||
.environmentObject(WorkoutManager.shared)
|
||||
.environmentObject(ConnectivityManager.shared)
|
||||
}
|
||||
|
||||
// Zones réelles de production, figées le 12/08/2026 — pour voir le rendu sans
|
||||
// lancer de séance ni brancher la montre. Ouvre ce fichier dans Xcode et
|
||||
// active le canevas (⌥⌘↩) : les cinq états s'affichent d'un coup.
|
||||
private let previewZones = HeartRateZones(
|
||||
z1: [100, 109], z2: [109, 118], z3: [118, 126],
|
||||
z4: [126, 134], z5: [134, 143],
|
||||
fcmaxUsed: 143, clinicalTargetBpm: 109
|
||||
)
|
||||
|
||||
#Preview("FC · les 5 zones") {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
// 104 → Z1, 115 → Z2 (la cible des séances CdC), 122 → Z3,
|
||||
// 130 → Z4, 141 → Z5. 109 vérifie la règle du chevauchement :
|
||||
// c'est le plancher de Z2, il ne doit PAS s'afficher Z1.
|
||||
ForEach([104, 109, 115, 122, 130, 141], id: \.self) { bpm in
|
||||
HeartRateMetric(bpm: bpm, zones: previewZones)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("FC · zones inconnues") {
|
||||
// Repli quand l'iPhone n'a jamais poussé de snapshot : la FC reste
|
||||
// lisible, en rouge, sans zone inventée.
|
||||
HeartRateMetric(bpm: 128, zones: nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user