From 54aca14fd4a7a093456574c128dce9a656cd6fca Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Sat, 15 Aug 2026 12:30:25 +0100 Subject: [PATCH] Trends: anchor empty/loading/error states to the top of the chart slot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The no-data states previously floated or centred vertically (Spacer sandwich), so the sheet rendered them halfway down the page while the chart-with-data anchors to the top. All chartArea states now share one top-aligned container that fills the space below the pickers — the empty state sits exactly where the chart will render, and the layout no longer jumps between states. 83 tests, Release build green. --- FuelBoard/TrendsView.swift | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/FuelBoard/TrendsView.swift b/FuelBoard/TrendsView.swift index 474d223..9ed58d4 100644 --- a/FuelBoard/TrendsView.swift +++ b/FuelBoard/TrendsView.swift @@ -147,32 +147,38 @@ struct TrendsView: View { @ViewBuilder private var chartArea: some View { - if isLoading { - Spacer() - ProgressView("Fetching price history…") - Spacer() - } else if loadFailed { - Spacer() - VStack(spacing: 10) { - Image(systemName: "wifi.exclamationmark") - .font(.system(size: 32)) - .foregroundStyle(.secondary) - Text("Couldn't load price history") - .font(.headline) - Button("Retry") { Task { await load() } } - .buttonStyle(.bordered) - } - Spacer() - } else if !hasAnyData { - emptyState - } else if !hasEnoughData { - emptyState // single point — nothing to draw yet - } else { - VStack(spacing: 12) { - chart - legend + // Every state anchors to the TOP of the chart slot — the same spot the + // chart occupies when data exists. The empty/loading/error states must + // not float or centre in the sheet, or the layout jumps between states. + Group { + if isLoading { + VStack(spacing: 12) { + ProgressView("Fetching price history…") + } + .padding(.top, 24) + } else if loadFailed { + VStack(spacing: 10) { + Image(systemName: "wifi.exclamationmark") + .font(.system(size: 32)) + .foregroundStyle(.secondary) + Text("Couldn't load price history") + .font(.headline) + Button("Retry") { Task { await load() } } + .buttonStyle(.bordered) + } + .padding(.top, 24) + } else if !hasAnyData { + emptyState + } else if !hasEnoughData { + emptyState // single point — nothing to draw yet + } else { + VStack(spacing: 12) { + chart + legend + } } } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) } private var emptyState: some View {