diff --git a/FuelBoard/StationDetailView.swift b/FuelBoard/StationDetailView.swift index 5c698a0..8eefea9 100644 --- a/FuelBoard/StationDetailView.swift +++ b/FuelBoard/StationDetailView.swift @@ -118,6 +118,37 @@ struct StationDetailView: View { .accessibilityHint(String(localized: "Opens Apple Maps with driving directions", comment: "Accessibility hint for the tappable map preview in the More info sheet")) } + // Floating "Copied" confirmation: a capsule hovering over the + // bottom of the map, directly above the Address row and + // horizontally centred. It is an overlay, so nothing moves + // while it is up, and `allowsHitTesting(false)` keeps the + // map's tap target live underneath. The map's clipShape is + // applied BEFORE this, so the capsule is not clipped to the + // map's rounded rect. + .overlay(alignment: .bottom) { + if showingCopied { + HStack(spacing: 4) { + Image(systemName: "checkmark") + Text("Copied") + } + .font(.footnote.weight(.semibold)) + .foregroundStyle(.green) + .padding(.horizontal, 10) + .padding(.vertical, 6) + // A material base keeps it legible over map tiles: a + // 14% green wash alone (the notice-capsule recipe) + // vanishes against the map. + .background { + Capsule() + .fill(.regularMaterial) + .overlay(Capsule().fill(Color.green.opacity(0.22))) + } + .padding(.bottom, 12) + .allowsHitTesting(false) + .accessibilityHidden(true) + .transition(.opacity) + } + } // No hairline between the map and the Address row: the map // already reads as its own block, so the line only added // clutter. The Address/Distance and Distance/actions lines @@ -135,31 +166,25 @@ struct StationDetailView: View { // // The whole row is a copy control: it puts JUST the postcode // on the clipboard so it can be pasted into another - // navigation app. The copy glyph and its "Copied" - // confirmation share a SECOND line that is always there, so - // the confirmation can never reflow the address above it. + // navigation app. The copy glyph sits ON the address line, + // and the "Copied" confirmation is a floating capsule over + // the map's bottom edge (see the map overlay above), so the + // confirmation can never reflow this row. Button { copyPostcode() } label: { - VStack(alignment: .leading, spacing: 6) { - HStack(alignment: .firstTextBaseline, spacing: 8) { - Text("Address") - Spacer(minLength: 8) - Text(addressLine) - .multilineTextAlignment(.trailing) - .foregroundStyle(.secondary) - } - HStack(spacing: 4) { - Spacer(minLength: 0) - if showingCopied { - Text("Copied") - .foregroundStyle(.green) - .transition(.opacity) - } - Image(systemName: showingCopied ? "checkmark" : "doc.on.doc") - .foregroundStyle(showingCopied ? Color.green : Color.secondary) - } - .font(.footnote.weight(.semibold)) + HStack(alignment: .firstTextBaseline, spacing: 8) { + Text("Address") + Spacer(minLength: 8) + Text(addressLine) + .multilineTextAlignment(.trailing) + .foregroundStyle(.secondary) + // Stays the plain copy glyph — the capsule carries + // the confirmation, so swapping this to a checkmark + // would say it twice. + Image(systemName: "doc.on.doc") + .font(.footnote.weight(.semibold)) + .foregroundStyle(.secondary) } } .buttonStyle(.plain)