live activity: select rich vs compact by actual GeometryReader width

ViewThatFits measures each child's IDEAL (untruncated) width to decide
fit, and a flexible .frame(maxWidth:.infinity) middle column does not cap
that ideal — so a long station name (e.g. 5-mi Phoenix Filling Stations)
inflated richBody's ideal past the iPhone Lock Screen width and fell back
to the compact strip. Replace the ViewThatFits branch with a
GeometryReader that reads the ACTUAL proposed width and shows the full
three-column card at/above 280pt, compact below. Content-independent, so a
long name can no longer collapse the iPhone card.
This commit is contained in:
FuelBoard Contributor
2026-08-19 22:08:07 +01:00
parent 227cce4aa6
commit a6620d36f7
@@ -65,27 +65,26 @@ struct FuelBoardLiveActivity: Widget {
private struct FuelBoardLiveActivityView: View {
let context: ActivityViewContext<FuelBoardLiveActivityAttributes>
/// Full card needs at least this much width; iPhone/iPad Lock Screen is
/// always wider, so they always get it. Only genuinely small slots
/// (CarPlay small / Watch smart stack) fall through to `compactBody`.
private let richMinWidth: CGFloat = 280
/// Cap the compact strip so a mid-width surface can't sneak it in.
private let compactMaxWidth: CGFloat = 230
/// 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).
private let compactWidthThreshold: CGFloat = 280
var body: some View {
Link(destination: context.state.mapsURL) {
ViewThatFits(in: .horizontal) {
// rich FIRST wins on the full-width iPhone/iPad Lock Screen.
// Its middle column is flexible + line-limited, so a LONG
// station name truncates in place and never inflates the ideal
// width (which used to trick ViewThatFits into falling back to
// compact on iPhone).
richBody
.frame(minWidth: richMinWidth)
// compact fallback the small CarPlay / Watch smart-stack
// strip: fuel · price · station distance (no app name).
compactBody
.frame(maxWidth: compactMaxWidth)
GeometryReader { geo in
// 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.
if geo.size.width >= compactWidthThreshold {
richBody
} else {
compactBody
}
}
}
}