Piloter la séance depuis la Live Activity (Pause / Terminer)
Comme l'app Exercice : deux boutons sur la bannière de l'écran verrouillé et dans l'île dépliée. iOS n'expose aucun geste de balayage pour révéler des actions — depuis iOS 17, ce sont des boutons intégrés (App Intents), et c'est le seul mécanisme public. - Intents `CoachTogglePauseIntent` / `CoachEndWorkoutIntent`, conformes à LiveActivityIntent (sans quoi `perform()` n'est jamais appelé). - Ils vivent dans CoachLiveActivityAttributes.swift, seul fichier déjà membre des deux targets : un fichier neuf imposerait une manip Target Membership dans Xcode, source d'erreurs répétées ici. - Commandes relayées à la montre par WCSession, avec repli transferUserInfo : isReachable retombe à false quand la séance tourne en arrière-plan profond, la commande est alors différée au réveil de l'app montre. - L'état de pause remonte depuis HKWorkoutSession (seule source fiable : la montre peut mettre en pause d'elle-même) et bascule le libellé du bouton. - Le watchdog de LiveStore est neutralisé pendant la pause : sans ça, l'absence de samples aurait affiché « connexion perdue » puis terminé l'activité. - L'app watchOS gagne le bouton Pause qui lui manquait, aligné sur le même état. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,18 +41,21 @@ struct CoachLiveActivityWidget: Widget {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
HStack(spacing: 18) {
|
||||
metricInline(label: "Distance",
|
||||
value: distKmText(context.state),
|
||||
unit: "km",
|
||||
color: .orange)
|
||||
Divider().frame(height: 28)
|
||||
metricInline(label: "Durée",
|
||||
value: elapsedText(context.state.elapsedSec),
|
||||
unit: "",
|
||||
color: .white)
|
||||
VStack(spacing: 10) {
|
||||
HStack(spacing: 18) {
|
||||
metricInline(label: "Distance",
|
||||
value: distKmText(context.state),
|
||||
unit: "km",
|
||||
color: .orange)
|
||||
Divider().frame(height: 28)
|
||||
metricInline(label: "Durée",
|
||||
value: elapsedText(context.state.elapsedSec),
|
||||
unit: "",
|
||||
color: .white)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
controls(state: context.state)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
} compactLeading: {
|
||||
HStack(spacing: 3) {
|
||||
@@ -101,11 +104,41 @@ struct CoachLiveActivityWidget: Widget {
|
||||
metricBlock(value: distKmText(context.state), unit: "km", color: .orange)
|
||||
metricBlock(value: elapsedText(context.state.elapsedSec), unit: "", color: .white)
|
||||
}
|
||||
controls(state: context.state)
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
|
||||
// MARK: - Contrôles (iOS 17+)
|
||||
|
||||
/// Pause/Reprendre et Terminer, comme l'app Exercice.
|
||||
/// Sur iOS 16, ActivityKit n'accepte aucun bouton : la Live Activity reste
|
||||
/// purement informative, sans rien casser.
|
||||
@ViewBuilder
|
||||
private func controls(state: CoachLiveActivityAttributes.ContentState) -> some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
HStack(spacing: 10) {
|
||||
Button(intent: CoachTogglePauseIntent(resuming: state.isPaused)) {
|
||||
Label(state.isPaused ? "Reprendre" : "Pause",
|
||||
systemImage: state.isPaused ? "play.fill" : "pause.fill")
|
||||
.font(.caption.weight(.semibold))
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.tint(state.isPaused ? .green : .yellow)
|
||||
|
||||
Button(intent: CoachEndWorkoutIntent()) {
|
||||
Label("Terminer", systemImage: "stop.fill")
|
||||
.font(.caption.weight(.semibold))
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func metricBlock(value: String, unit: String, color: Color) -> some View {
|
||||
VStack(spacing: 0) {
|
||||
|
||||
Reference in New Issue
Block a user