More info sheet: float the "Copied" capsule and put the copy glyph on the address line

The address row is back to a single line: the copy glyph now sits on the address
line itself (trailing, plain glyph — the confirmation says it elsewhere), and the
always-reserved second line is gone.

The "Copied" confirmation is now a green-tinted capsule that floats over the
bottom of the map, directly above the address and horizontally centred. It is an
overlay with allowsHitTesting(false), so nothing reflows while it is up (verified:
the address row's ink, column for column, does not move) and the map's tap target
stays live underneath (verified: tapping where the capsule floats still opens
Apple Maps). A material base keeps it legible against map tiles, where a 14% wash
alone — the notice-capsule recipe — disappears. Timing is unchanged: 2s, then
fades, with the success haptic on copy.
This commit is contained in:
FuelBoard Contributor
2026-10-14 13:45:05 +01:00
parent 73db893f4f
commit 6e534ed5b7
+47 -22
View File
@@ -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)