diff --git a/FuelBoard/StationDetailView.swift b/FuelBoard/StationDetailView.swift index ffa6b95..e222ec5 100644 --- a/FuelBoard/StationDetailView.swift +++ b/FuelBoard/StationDetailView.swift @@ -84,33 +84,40 @@ struct StationDetailView: View { NavigationStack { List { Section { - // Tapping the map opens the same Apple Maps directions the - // Directions button below opens. The Map still ignores - // hit testing itself so the wrapping Button — not MapKit — - // owns the tap; the map is a preview, not something to pan. - Button { - openDirections() - } label: { - Map(position: .constant(.region(MKCoordinateRegion( - center: stationCoordinate, - span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) - )))) { - Marker(station.name, coordinate: stationCoordinate) - .tint(.red) - } - .frame(height: 180) - .clipShape(RoundedRectangle(cornerRadius: 12)) - .allowsHitTesting(false) + // Tapping anywhere on the map opens the same Apple Maps + // directions the Directions button below opens. + // + // The tap target is an OVERLAY Button laid ON TOP of the + // Map, not a Button wrapping it. A Map runs its own UIKit + // view, and inside a List row the wrapped-Map form left the + // card untappable — the touch never reached the button. An + // overlay sits above the map, owns an explicit rounded-rect + // content shape, and cannot be intercepted by MapKit. The + // Map itself stays non-interactive: it is a preview, not + // something to pan. + Map(position: .constant(.region(MKCoordinateRegion( + center: stationCoordinate, + span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02) + )))) { + Marker(station.name, coordinate: stationCoordinate) + .tint(.red) + } + .frame(height: 180) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .allowsHitTesting(false) + .overlay { + Button { + openDirections() + } label: { + Color.clear + .contentShape(RoundedRectangle(cornerRadius: 12)) + } + .buttonStyle(.plain) + .accessibilityLabel(String(localized: "Directions to \(station.name)", + comment: "Accessibility label for the tappable map preview in the More info sheet")) + .accessibilityHint(String(localized: "Opens Apple Maps with driving directions", + comment: "Accessibility hint for the tappable map preview in the More info sheet")) } - .buttonStyle(.plain) - // The hit area is declared on the BUTTON, not inside the - // label: the label's Map has hit testing switched off, so - // without this the card could fall through to the row. - .contentShape(RoundedRectangle(cornerRadius: 12)) - .accessibilityLabel(String(localized: "Directions to \(station.name)", - comment: "Accessibility label for the tappable map preview in the More info sheet")) - .accessibilityHint(String(localized: "Opens Apple Maps with driving directions", - comment: "Accessibility hint for the tappable map preview in the More info sheet")) .listRowInsets(EdgeInsets()) } Section { @@ -121,28 +128,31 @@ 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 trailing icon marks it tappable and - // becomes the "Copied" confirmation for a couple of seconds. + // 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. Button { copyPostcode() } label: { - HStack(alignment: .firstTextBaseline, spacing: 8) { - Text("Address") - Spacer(minLength: 8) - Text(addressLine) - .multilineTextAlignment(.trailing) - .foregroundStyle(.secondary) + 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") - .font(.footnote.weight(.semibold)) .foregroundStyle(.green) .transition(.opacity) } Image(systemName: showingCopied ? "checkmark" : "doc.on.doc") - .font(.footnote.weight(.semibold)) .foregroundStyle(showingCopied ? Color.green : Color.secondary) } + .font(.footnote.weight(.semibold)) } } .buttonStyle(.plain)