Use native large-title behaviour: drop custom icon header, .navigationTitle + .large (exact Mail-style collapse)
This commit is contained in:
@@ -22,35 +22,10 @@ struct StationsView: View {
|
||||
/// which station is "best".
|
||||
@State private var pageSize = 10
|
||||
@State private var visibleCount = 10
|
||||
/// True once the list has scrolled — the large header title is shown at
|
||||
/// rest; the compact gas-pump+title bar appears only after scrolling.
|
||||
@State private var isScrolled = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 0) {
|
||||
// Large pinned title — Mail-style. It stays in place above the
|
||||
// list (content scrolls beneath it) and fades out in place
|
||||
// once scrolling starts, while the compact icon+title bar fades
|
||||
// in below the nav bar. Keeping it OUT of the List means the
|
||||
// fade isn't masked by the row scrolling away.
|
||||
HStack(spacing: 10) {
|
||||
Image(systemName: "fuelpump.fill")
|
||||
.font(.system(size: 34))
|
||||
.foregroundStyle(selectedFuel.tintColor)
|
||||
Text("FuelBoard")
|
||||
.font(.largeTitle.bold())
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 6)
|
||||
.frame(height: isScrolled ? 0 : 54, alignment: .top)
|
||||
.clipped()
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.opacity(isScrolled ? 0 : 1)
|
||||
.animation(.easeInOut(duration: 0.2), value: isScrolled)
|
||||
|
||||
List {
|
||||
List {
|
||||
Section {
|
||||
if let location {
|
||||
if sortMode == .closest {
|
||||
@@ -205,51 +180,18 @@ struct StationsView: View {
|
||||
// New fetch or filter switch → back to the first page.
|
||||
visibleCount = pageSize
|
||||
}
|
||||
.modifier(ScrolledTracker(isScrolled: $isScrolled))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
if isScrolled {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "fuelpump.fill")
|
||||
.foregroundStyle(selectedFuel.tintColor)
|
||||
Text("FuelBoard")
|
||||
.font(.headline)
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
// Native large-title behaviour (Mail-style): the system shows the
|
||||
// large title at rest, scrolls it away and fades the compact
|
||||
// inline title in. No custom toolbar needed — the native mechanism
|
||||
// only renders text, which is exactly what we want here.
|
||||
.navigationTitle("FuelBoard")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Fuel-type iconography (app target only — FuelStore.swift is Foundation-only)
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FuelType {
|
||||
/// Short segment label ("Unleaded" / "Premium" / "Diesel") for the picker.
|
||||
var shortName: String {
|
||||
|
||||
Reference in New Issue
Block a user