Fix Live Activity in CarPlay: show real bubble and text via .small family

CarPlay renders Live Activities through the .small activity family
(shared with the Watch Smart Stack), not the lock-screen content view.
Without it, CarPlay fell back to the Dynamic Island's compactLeading/
compactTrailing, which only had the generic "level" SF Symbol and an
empty trailing view — hence the wrong icon and missing text.

Adds .supplementalActivityFamilies([.small]) and a compact layout that
reuses the real LevelBubbleGlyph plus a one-line instruction for that
family, while the lock screen keeps its existing two-line view.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
fototeddy
2026-09-05 11:39:08 +02:00
co-authored by Claude Sonnet 5
parent 362a62cd77
commit 004e64e933
2 changed files with 56 additions and 6 deletions
@@ -81,7 +81,7 @@ private struct LevelBubbleGlyph: View {
struct VanAligneiOSWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LevelActivityAttributes.self) { context in
LockScreenLevelView(attributes: context.attributes, state: context.state)
LevelActivityContent(attributes: context.attributes, state: context.state)
.activityBackgroundTint(Color(white: 0.08))
.activitySystemActionForegroundColor(.white)
@@ -109,12 +109,60 @@ struct VanAligneiOSWidgetLiveActivity: Widget {
}
.keylineTint(context.state.isLevel ? .green : .orange)
}
.supplementalActivityFamilies([.small])
}
}
/// Sperrbildschirm- und Banner-Ansicht. Dieselbe Ansicht landet automatisch
/// auch im CarPlay-Dashboard, sobald das iPhone verbunden ist ein eigenes
/// CarPlay-App-Target ist dafür nicht nötig.
/// Wählt je nach Darstellungsgrösse zwischen Sperrbildschirm- und
/// Kompaktansicht.
///
/// Ohne `.small`-Familie fällt CarPlay (wie die Smart Stack der Uhr) auf
/// `compactLeading`/`compactTrailing` der Dynamic Island zurück dort steht
/// nur das generische Symbol und, weil `compactTrailing` leer bleibt, gar
/// kein Text. Erst die `.small`-Familie liefert CarPlay Libelle und Text.
private struct LevelActivityContent: View {
let attributes: LevelActivityAttributes
let state: LevelActivityAttributes.ContentState
@Environment(\.activityFamily) private var activityFamily
var body: some View {
switch activityFamily {
case .small:
CompactLevelView(attributes: attributes, state: state)
default:
LockScreenLevelView(attributes: attributes, state: state)
}
}
}
/// Kompaktform für CarPlay-Dashboard und Smart Stack der Uhr: Libelle plus
/// eine Textzeile, ohne die längere Zwei-Zeilen-Aufschlüsselung des
/// Sperrbildschirms.
private struct CompactLevelView: View {
let attributes: LevelActivityAttributes
let state: LevelActivityAttributes.ContentState
var body: some View {
HStack(spacing: 12) {
LevelBubbleGlyph(pitch: state.pitch, roll: state.roll, isLevel: state.isLevel, diameter: 40)
Text(state.instruction
?? (state.pitch == nil && state.roll == nil ? "Keine Messwerte" : attributes.deviceName))
.font(.callout.weight(.semibold))
.foregroundStyle(state.isLevel ? .green : .white)
.lineLimit(1)
.minimumScaleFactor(0.8)
Spacer(minLength: 0)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
.foregroundStyle(.white)
}
}
/// Sperrbildschirm- und Banner-Ansicht.
private struct LockScreenLevelView: View {
let attributes: LevelActivityAttributes
let state: LevelActivityAttributes.ContentState