From cf693504727872b664b67515377b2d0a55374605 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Tue, 11 Aug 2026 22:05:03 +0100 Subject: [PATCH] Use native onScrollGeometryChange (iOS 18) so the compact icon+title bar actually appears on scroll, Mail-style --- FuelBoard/StationsView.swift | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/FuelBoard/StationsView.swift b/FuelBoard/StationsView.swift index 4a693ca..c6e8e60 100644 --- a/FuelBoard/StationsView.swift +++ b/FuelBoard/StationsView.swift @@ -30,8 +30,8 @@ struct StationsView: View { NavigationStack { List { // Large title header (icon + title). Native navigation titles - // can't carry an icon, so this is a real row; the scroll - // tracker rides on its background so no extra row is created. + // can't carry an icon, so this is a real row at the top of the + // list; it scrolls away like Mail's large title. HStack(spacing: 10) { Image(systemName: "fuelpump.fill") .font(.system(size: 34)) @@ -44,14 +44,6 @@ struct StationsView: View { .listRowSeparator(.hidden) .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) .listRowBackground(Color.clear) - .background( - GeometryReader { proxy in - Color.clear.preference( - key: ScrollOffsetKey.self, - value: proxy.frame(in: .named("stationsScroll")).minY - ) - } - ) Section { if let location { @@ -207,10 +199,7 @@ struct StationsView: View { // New fetch or filter switch → back to the first page. visibleCount = pageSize } - .coordinateSpace(name: "stationsScroll") - .onPreferenceChange(ScrollOffsetKey.self) { minY in - isScrolled = minY < -60 - } + .modifier(ScrolledTracker(isScrolled: $isScrolled)) .toolbar { ToolbarItem(placement: .principal) { if isScrolled { @@ -231,13 +220,26 @@ struct StationsView: View { // MARK: - Fuel-type iconography (app target only — FuelStore.swift is Foundation-only) -/// Scroll offset preference — the large-title header row reports its position -/// in the list's coordinate space so we know when it has scrolled under the -/// nav bar (minY < -60) and can show the compact gas-pump+title bar. -private struct ScrollOffsetKey: PreferenceKey { - static var defaultValue: CGFloat = 0 - static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - value = nextValue() +/// Tracks whether the list has scrolled, using the native scroll-geometry API +/// (iOS 18+) — the reliable way to know when the large header title has +/// collapsed. (The old GeometryReader-preference approach didn't fire in List, +/// which is why the compact bar never appeared.) +private struct ScrolledTracker: ViewModifier { + @Binding var isScrolled: Bool + + func body(content: Content) -> some View { + if #available(iOS 18.0, *) { + content + .onScrollGeometryChange(for: Bool.self) { geometry in + geometry.contentOffset.y > 40 + } action: { _, scrolled in + withAnimation(.easeInOut(duration: 0.2)) { + isScrolled = scrolled + } + } + } else { + content + } } }