Use native large-title behaviour: drop custom icon header, .navigationTitle + .large (exact Mail-style collapse)
This commit is contained in:
@@ -22,34 +22,9 @@ struct StationsView: View {
|
|||||||
/// which station is "best".
|
/// which station is "best".
|
||||||
@State private var pageSize = 10
|
@State private var pageSize = 10
|
||||||
@State private var visibleCount = 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 {
|
var body: some View {
|
||||||
NavigationStack {
|
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 {
|
Section {
|
||||||
if let location {
|
if let location {
|
||||||
@@ -205,51 +180,18 @@ 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
|
||||||
}
|
}
|
||||||
.modifier(ScrolledTracker(isScrolled: $isScrolled))
|
// Native large-title behaviour (Mail-style): the system shows the
|
||||||
.toolbar {
|
// large title at rest, scrolls it away and fades the compact
|
||||||
ToolbarItem(placement: .principal) {
|
// inline title in. No custom toolbar needed — the native mechanism
|
||||||
if isScrolled {
|
// only renders text, which is exactly what we want here.
|
||||||
HStack(spacing: 6) {
|
.navigationTitle("FuelBoard")
|
||||||
Image(systemName: "fuelpump.fill")
|
.navigationBarTitleDisplayMode(.large)
|
||||||
.foregroundStyle(selectedFuel.tintColor)
|
|
||||||
Text("FuelBoard")
|
|
||||||
.font(.headline)
|
|
||||||
}
|
|
||||||
.transition(.opacity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Fuel-type iconography (app target only — FuelStore.swift is Foundation-only)
|
// 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 {
|
extension FuelType {
|
||||||
/// Short segment label ("Unleaded" / "Premium" / "Diesel") for the picker.
|
/// Short segment label ("Unleaded" / "Premium" / "Diesel") for the picker.
|
||||||
var shortName: String {
|
var shortName: String {
|
||||||
|
|||||||
Reference in New Issue
Block a user