In the view described in the following code, I would like to be able to select a marker, no matter if the marker is a Place or an Accommodation. Both Place and Accommodation are defined as classes with different fields. Both of them have latitude, and longitude. But I can only make one of them selectable depending on which one I put on this line
Map(position: $cameraPosition, selection: $selectedAccommodation)
or
Map(position: $cameraPosition, selection: $selectedAccommodation)
import SwiftUI
import MapKit
struct DestinationMapView: View {
var destination: Destination
@State private var cameraPosition: MapCameraPosition = .automatic
@State private var selectedPlace: Place?
@State private var selectedAccommodation: Accommodation?
//@State private var selectedPlace: SelectablePlace?
var body: some View {
Map(position: $cameraPosition, selection: $selectedPlace) {
ForEach(destination.accommodations ?? []) { accommodation in
Group {
if let lat = accommodation.latitude, let lon = accommodation.longitude {
Marker(accommodation.name, systemImage: "bed.double.fill", coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon))
.tint(.blue)
}
}
.tag(accommodation)
}
ForEach(destination.places ?? []) { place in
Group {
if let lat = place.latitude, let lon = place.longitude {
Marker(place.name, systemImage: place.placeType.systemImage, coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon))
}
}
.tag(place)
}
}
.sheet(item: $selectedPlace) { selectedPlace in
Text("Selected Place")
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
.sheet(item: $selectedAccommodation) { selectedAccommodation in
Text("Selected Accommodation")
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
.onAppear {
if let coordinateRegion = destination.coordinateRegion {
cameraPosition = MapCameraPosition.region(coordinateRegion)
}
}
}
}
How can add the capability to select the marker no matter if it's of Place type or Accommodation type?
Try this approach using a dedicated Selection
that holds the kind
and the id
of the
selected Accommodation
or Place
. Adjust the code as required.
enum MarkerType { // <--- here
case place
case accommodation
}
struct Selection: Identifiable, Hashable { // <--- here
let id: UUID
let kind: MarkerType
}
struct DestinationMapView: View {
var destination: Destination
@State private var cameraPosition: MapCameraPosition = .automatic
@State private var selectedPlace: Place?
@State private var selectedAccommodation: Accommodation?
@State private var selection: Selection? // <--- here
var body: some View {
Map(position: $cameraPosition, selection: $selection) { // <--- here
ForEach(destination.accommodations ?? []) { accommodation in
Group {
if let lat = accommodation.latitude, let lon = accommodation.longitude {
Marker(accommodation.name, systemImage: "bed.double.fill", coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon))
.tint(.blue)
}
}
.tag(Selection(id: accommodation.id, kind: .accommodation))
}
ForEach(destination.places ?? []) { place in
Group {
if let lat = place.latitude, let lon = place.longitude {
Marker(place.name, systemImage: place.placeType, coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon))
}
}
.tag(Selection(id: place.id, kind: .place))
}
}
.sheet(item: $selection) { selection in // <--- here
if selection.kind == .accommodation {
Text("Selected Accommodation: \(getAccomodation(selection)?.name ?? "no name")")
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
} else {
Text("Selected Place: \(getPlace(selection)?.name ?? "no name")")
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
}
.onAppear {
if let coordinateRegion = destination.coordinateRegion {
cameraPosition = MapCameraPosition.region(coordinateRegion)
}
}
}
// --- here
func getAccomodation(_ selection: Selection?) -> Accommodation? {
if let select = selection, let acc = destination.accommodations?.first(where: {$0.id == select.id})
{
return acc
}
return nil
}
func getPlace(_ selection: Selection?) -> Place? {
if let select = selection, let place = destination.places?.first(where: {$0.id == select.id})
{
return place
}
return nil
}
}
// --- for testing
struct ContentView: View {
let destination = Destination(accommodations: [ Accommodation(name: "acc-1", latitude: 51.515, longitude: -0.117),
Accommodation(name: "acc-2", latitude: 51.516, longitude: -0.118),
Accommodation(name: "acc-3", latitude: 51.517, longitude: -0.119)],
places: [ Place(name: "place-1", latitude: 51.525, longitude: -0.127),
Place(name: "place-2", latitude: 51.526, longitude: -0.128),
Place(name: "place-3", latitude: 51.527, longitude: -0.129)],
coordinateRegion: MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.52, longitude: -0.116), latitudinalMeters: 3500, longitudinalMeters: 3500))
var body: some View {
DestinationMapView(destination: destination)
}
}
struct Place: Identifiable {
let id = UUID()
var name: String
var latitude: Double?
var longitude: Double?
var placeType: String = "house" // <--- for testing
}
struct Accommodation: Identifiable {
let id = UUID()
var name: String
var latitude: Double?
var longitude: Double?
}
struct Destination: Identifiable {
let id = UUID()
var accommodations: [Accommodation]?
var places: [Place]?
var coordinateRegion: MKCoordinateRegion?
}