Use native onScrollGeometryChange (iOS 18) so the compact icon+title bar actually appears on scroll, Mail-style
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user