Use native onScrollGeometryChange (iOS 18) so the compact icon+title bar actually appears on scroll, Mail-style

This commit is contained in:
FuelBoard Contributor
2026-08-11 22:05:03 +01:00
parent 8d24de401d
commit cf69350472
+23 -21
View File
@@ -30,8 +30,8 @@ struct StationsView: View {
NavigationStack { NavigationStack {
List { List {
// Large title header (icon + title). Native navigation titles // Large title header (icon + title). Native navigation titles
// can't carry an icon, so this is a real row; the scroll // can't carry an icon, so this is a real row at the top of the
// tracker rides on its background so no extra row is created. // list; it scrolls away like Mail's large title.
HStack(spacing: 10) { HStack(spacing: 10) {
Image(systemName: "fuelpump.fill") Image(systemName: "fuelpump.fill")
.font(.system(size: 34)) .font(.system(size: 34))
@@ -44,14 +44,6 @@ struct StationsView: View {
.listRowSeparator(.hidden) .listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.listRowBackground(Color.clear) .listRowBackground(Color.clear)
.background(
GeometryReader { proxy in
Color.clear.preference(
key: ScrollOffsetKey.self,
value: proxy.frame(in: .named("stationsScroll")).minY
)
}
)
Section { Section {
if let location { if let location {
@@ -207,10 +199,7 @@ struct StationsView: View {
// New fetch or filter switch back to the first page. // New fetch or filter switch back to the first page.
visibleCount = pageSize visibleCount = pageSize
} }
.coordinateSpace(name: "stationsScroll") .modifier(ScrolledTracker(isScrolled: $isScrolled))
.onPreferenceChange(ScrollOffsetKey.self) { minY in
isScrolled = minY < -60
}
.toolbar { .toolbar {
ToolbarItem(placement: .principal) { ToolbarItem(placement: .principal) {
if isScrolled { if isScrolled {
@@ -231,13 +220,26 @@ struct StationsView: View {
// MARK: - Fuel-type iconography (app target only FuelStore.swift is Foundation-only) // MARK: - Fuel-type iconography (app target only FuelStore.swift is Foundation-only)
/// Scroll offset preference the large-title header row reports its position /// Tracks whether the list has scrolled, using the native scroll-geometry API
/// in the list's coordinate space so we know when it has scrolled under the /// (iOS 18+) the reliable way to know when the large header title has
/// nav bar (minY < -60) and can show the compact gas-pump+title bar. /// collapsed. (The old GeometryReader-preference approach didn't fire in List,
private struct ScrollOffsetKey: PreferenceKey { /// which is why the compact bar never appeared.)
static var defaultValue: CGFloat = 0 private struct ScrolledTracker: ViewModifier {
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { @Binding var isScrolled: Bool
value = nextValue()
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
}
} }
} }