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:
co-authored by
Claude Sonnet 5
parent
45aa7cf984
commit
46a4da5e37
@@ -5,9 +5,11 @@ import Observation
|
|||||||
/// Startet, aktualisiert und beendet die Live Activity des Neigungsmessers.
|
/// Startet, aktualisiert und beendet die Live Activity des Neigungsmessers.
|
||||||
///
|
///
|
||||||
/// Es läuft höchstens eine Aktivität gleichzeitig – mehr als einen
|
/// Es läuft höchstens eine Aktivität gleichzeitig – mehr als einen
|
||||||
/// Neigungsmesser gibt es im aktiven Profil ohnehin nicht. Seit iOS 17 zeigt
|
/// Neigungsmesser gibt es im aktiven Profil ohnehin nicht. Seit iOS 26 zeigt
|
||||||
/// CarPlay eine laufende Live Activity automatisch im Dashboard an; ein
|
/// CarPlay eine laufende Live Activity automatisch im Dashboard an; ein
|
||||||
/// eigenes CarPlay-App-Target braucht es dafür nicht.
|
/// eigenes CarPlay-App-Target braucht es dafür nicht – die Inhalte kommen
|
||||||
|
/// aber nicht von der Sperrbildschirm-Ansicht, sondern von der
|
||||||
|
/// `.small`-Aktivitätsfamilie (siehe `VanAligneiOSWidgetLiveActivity`).
|
||||||
@Observable
|
@Observable
|
||||||
final class LevelActivityManager {
|
final class LevelActivityManager {
|
||||||
private(set) var trackedDeviceID: UUID?
|
private(set) var trackedDeviceID: UUID?
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ private struct LevelBubbleGlyph: View {
|
|||||||
struct VanAligneiOSWidgetLiveActivity: Widget {
|
struct VanAligneiOSWidgetLiveActivity: Widget {
|
||||||
var body: some WidgetConfiguration {
|
var body: some WidgetConfiguration {
|
||||||
ActivityConfiguration(for: LevelActivityAttributes.self) { context in
|
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))
|
.activityBackgroundTint(Color(white: 0.08))
|
||||||
.activitySystemActionForegroundColor(.white)
|
.activitySystemActionForegroundColor(.white)
|
||||||
|
|
||||||
@@ -109,12 +109,60 @@ struct VanAligneiOSWidgetLiveActivity: Widget {
|
|||||||
}
|
}
|
||||||
.keylineTint(context.state.isLevel ? .green : .orange)
|
.keylineTint(context.state.isLevel ? .green : .orange)
|
||||||
}
|
}
|
||||||
|
.supplementalActivityFamilies([.small])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sperrbildschirm- und Banner-Ansicht. Dieselbe Ansicht landet automatisch
|
/// Wählt je nach Darstellungsgrösse zwischen Sperrbildschirm- und
|
||||||
/// auch im CarPlay-Dashboard, sobald das iPhone verbunden ist – ein eigenes
|
/// Kompaktansicht.
|
||||||
/// CarPlay-App-Target ist dafür nicht nötig.
|
///
|
||||||
|
/// 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 {
|
private struct LockScreenLevelView: View {
|
||||||
let attributes: LevelActivityAttributes
|
let attributes: LevelActivityAttributes
|
||||||
let state: LevelActivityAttributes.ContentState
|
let state: LevelActivityAttributes.ContentState
|
||||||
|
|||||||
Reference in New Issue
Block a user