diff --git a/FuelBoardWidgets/FuelBoardLiveActivityView.swift b/FuelBoardWidgets/FuelBoardLiveActivityView.swift index 0e191ca..8b214f4 100644 --- a/FuelBoardWidgets/FuelBoardLiveActivityView.swift +++ b/FuelBoardWidgets/FuelBoardLiveActivityView.swift @@ -67,32 +67,46 @@ private struct FuelBoardLiveActivityView: View { /// Below this ACTUAL proposed width we show the compact strip (CarPlay /// small / Watch smart stack); at/above it we show the full card. The - /// decision is made from the real space the system hands the body via - /// GeometryReader, NOT from ViewThatFits ideal-width measurement — the - /// latter is broken for truncating text (a long station name inflates the - /// "ideal" width past the iPhone Lock Screen and collapses the full card). + /// decision is made from the real width the system hands the body, read + /// via a background GeometryReader — NOT ViewThatFits ideal-width + /// measurement (that's broken for truncating text: a long station name + /// inflated the "ideal" width past the iPhone Lock Screen and collapsed + /// the full card). private let compactWidthThreshold: CGFloat = 280 + /// Measured slot width (drives the rich-vs-compact branch). Measured in a + /// background GeometryReader so it does NOT act as the layout container: + /// a GeometryReader root pins content top-left, and forcing a + /// maxHeight:.infinity frame on it over-claims the whole proposed height, + /// centring the content below true vertical centre (bigger gap above) on + /// the Lock Screen. Measuring behind the scenes keeps the content + /// intrinsic-sized so the system vertically centres it itself. + @State private var slotWidth: CGFloat = 400 + var body: some View { Link(destination: context.state.mapsURL) { - GeometryReader { geo in + Group { // Branch on the ACTUAL proposed width. iPhone/iPad offer the // full Lock Screen width (>= threshold) → rich card, no matter // how long the station name is. Truly small slots (CarPlay / // Watch) offer much less → compact strip. - Group { - if geo.size.width >= compactWidthThreshold { - richBody - } else { - compactBody - } + if slotWidth >= compactWidthThreshold { + richBody + } else { + compactBody } - // A GeometryReader hands its content the FULL proposed frame - // (content is otherwise pinned top-left), so centre the card - // within that space — keeps the Lock Screen content vertically - // aligned instead of sitting high. - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) } + // Fill the card width so the background measure reads the real + // slot, not the intrinsic content width. + .frame(maxWidth: .infinity) + // Side-channel width measurement — never the layout container. + .background( + GeometryReader { geo in + Color.clear + .onAppear { slotWidth = geo.size.width } + .onChange(of: geo.size.width) { _, w in slotWidth = w } + } + ) } }