Use native large-title behaviour: drop custom icon header, .navigationTitle + .large (exact Mail-style collapse)

This commit is contained in:
FuelBoard Contributor
2026-08-11 22:27:38 +01:00
parent 24fffe1e8f
commit 313fe84037
+6 -64
View File
@@ -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 {