Моя цель:
- Пользователь нажимает на отметку на карте.
- Над отметкой появляется настраиваемое представление выноски.
- Это представление выноски содержит кнопку «Показать детали».
- Когда пользователь нажимает на нее При нажатии кнопки отображается лист с дополнительной информацией.
- Если пользователь нажимает где-нибудь еще на карте (не на выноске), выноска должна быть закрыта.
Мое текущее решение — представить лист сведений одновременно с выноской (т. е. при нажатии самой булавки), что не является желаемым для пользователя. Пользователь должен сначала увидеть выноску, а затем выбрать просмотр более подробной информации, нажав кнопку.
Как сделать кнопку внутри моего пользовательского CalloutView (которая представлена в виде .overlay MapAnnotation) интерактивной, чтобы она могла инициировать представление листа?
Я бы предпочел решение, использующее только SwiftUI. Однако, если это известное ограничение, которое невозможно преодолеть, я открыт для решения, основанного на UIKit (MKMapView), но я был бы признателен за объяснение того, почему чистый подход SwiftUI невозможен.
import SwiftUI
import MapKit
struct Location: Identifiable, Equatable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
static func == (lhs: Location, rhs: Location) -> Bool {
lhs.id == rhs.id
}
}
// Presented as a sheet
struct LocationDetailView: View {
let location: Location
var body: some View {
VStack(spacing: 16) {
Text(location.name)
.font(.largeTitle)
Text("Details about \(location.name) would be displayed here.")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
.navigationTitle("Location Details")
.navigationBarTitleDisplayMode(.inline)
}
}
// Custom Callout View
struct CustomCalloutView: View {
let location: Location
var onDetailsTapped: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(location.name)
.font(.headline)
// Button action is never called.
Button(action: {
print("Info button tapped for \(location.name)")
onDetailsTapped()
}) {
HStack {
Text("Show Details")
Image(systemName: "info.circle")
}
.padding(.vertical, 8)
.padding(.horizontal, 12)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.buttonStyle(.plain)
}
.padding(12)
.background(Color(uiColor: .systemBackground))
.cornerRadius(10)
.shadow(radius: 5)
.frame(width: 200)
}
}
struct MapContentView: View {
@State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 34.0522, longitude: -118.2437), // Los Angeles
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
private let locations: [Location] = [
.init(name: "City Hall", coordinate: .init(latitude: 34.0522, longitude: -118.2437)),
.init(name: "Dodger Stadium", coordinate: .init(latitude: 34.0739, longitude: -118.2400)),
.init(name: "Santa Monica Pier", coordinate: .init(latitude: 34.0100, longitude: -118.4969))
]
@State private var selectedLocation: Location?
@State private var locationForSheet: Location?
var body: some View {
NavigationView {
Map(coordinateRegion: $region, annotationItems: locations) { location in
MapAnnotation(coordinate: location.coordinate) {
Image(systemName: "mappin.and.ellipse")
.font(.title)
.foregroundColor(.red)
.background(Circle().fill(Color.white))
.shadow(radius: 1)
.overlay(
Group {
if selectedLocation == location {
CustomCalloutView(location: location) {
self.locationForSheet = location
}
.offset(y: -55)
}
}
)
.onTapGesture {
print("Pin Tapped: \(location.name)")
withAnimation(.spring()) {
selectedLocation = location
}
// Current Workaround:
// Because the callout button doesn't work, the sheet
// is triggered at the same time as the callout.
// This is the behavior I want to avoid.
self.locationForSheet = location
}
}
}
.navigationTitle("Map Example")
.navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea(edges: .bottom)
.sheet(item: $locationForSheet) { location in
NavigationView {
LocationDetailView(location: location)
}
}
.simultaneousGesture(
// Tap gesture on the map to dismiss the callout
TapGesture().onEnded { _ in
print("Map Tapped: Dismissing callout")
withAnimation(.spring()) {
selectedLocation = nil
}
}
)
}
}
}
#Preview {
MapContentView()
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... ot-working
Мобильная версия