Large icon+title header at rest; compact gas-pump bar appears only after scrolling (tracker on header row background)
This commit is contained in:
@@ -22,10 +22,37 @@ 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 {
|
||||||
List {
|
List {
|
||||||
|
// Large title header (icon + title). Native navigation titles
|
||||||
|
// can't carry an icon, so this is a real row; the scroll
|
||||||
|
// tracker rides on its background so no extra row is created.
|
||||||
|
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(.vertical, 6)
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
|
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||||
|
.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 {
|
||||||
if sortMode == .closest {
|
if sortMode == .closest {
|
||||||
@@ -180,24 +207,40 @@ 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")
|
||||||
|
.onPreferenceChange(ScrollOffsetKey.self) { minY in
|
||||||
|
isScrolled = minY < -60
|
||||||
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
|
if isScrolled {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
Image(systemName: "fuelpump.fill")
|
Image(systemName: "fuelpump.fill")
|
||||||
.foregroundStyle(selectedFuel.tintColor)
|
.foregroundStyle(selectedFuel.tintColor)
|
||||||
Text("FuelBoard")
|
Text("FuelBoard")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
}
|
}
|
||||||
|
.transition(.opacity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.navigationTitle("FuelBoard")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
/// in the list's coordinate space so we know when it has scrolled under the
|
||||||
|
/// nav bar (minY < -60) and can show the compact gas-pump+title bar.
|
||||||
|
private struct ScrollOffsetKey: PreferenceKey {
|
||||||
|
static var defaultValue: CGFloat = 0
|
||||||
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||||
|
value = nextValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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