В SwiftUI DSL вы не получаете доступ к представлениям.
Вместо этого вы комбинируете их "представления" для создания представлений.
Пин может быть представлен объектом - манипулируя пиномтакже обновит карту.
Это наш пин-объект:
class MapPin: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
let subtitle: String?
let action: (() -> Void)?
init(coordinate: CLLocationCoordinate2D,
title: String? = nil,
subtitle: String? = nil,
action: (() -> Void)? = nil) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.action = action
}
}
Вот мой Map
, который не просто UIViewRepresentable
, но также использует Coordinator
.
(Подробнее о UIViewRepresentable
и координаторах можно найти в отличном выступлении на WWDC 2019 - Интеграция SwiftUI )
struct Map : UIViewRepresentable {
class Coordinator: NSObject, MKMapViewDelegate {
@Binding var selectedPin: MapPin?
init(selectedPin: Binding<MapPin?>) {
$selectedPin = selectedPin
}
func mapView(_ mapView: MKMapView,
didSelect view: MKAnnotationView) {
guard let pin = view.annotation as? MapPin else {
return
}
pin.action?()
selectedPin = pin
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard (view.annotation as? MapPin) != nil else {
return
}
selectedPin = nil
}
}
@Binding var pins: [MapPin]
@Binding var selectedPin: MapPin?
func makeCoordinator() -> Coordinator {
return Coordinator(selectedPin: $selectedPin)
}
func makeUIView(context: Context) -> MKMapView {
let view = MKMapView(frame: .zero)
view.delegate = context.coordinator
return view
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.removeAnnotations(uiView.annotations)
uiView.addAnnotations(pins)
if let selectedPin = selectedPin {
uiView.selectAnnotation(selectedPin, animated: false)
}
}
}
Идея:
- Пины имеют вид
@State
на виде, содержащем карту, и передаются в виде привязки. - Каждый раз, когда пин-код добавляется или удаляется, он вызывает обновление пользовательского интерфейса -все контакты будут удалены, а затем добавлены снова (не очень эффективно, но это выходит за рамки этого ответа)
Coordinator
- делегат карты - я могу получить затронутый MapPin
от делегатаметоды.
Чтобы проверить это :
struct ContentView: View {
@State var pins: [MapPin] = [
MapPin(coordinate: CLLocationCoordinate2D(latitude: 51.509865,
longitude: -0.118092),
title: "London",
subtitle: "Big Smoke",
action: { print("Hey mate!") } )
]
@State var selectedPin: MapPin?
var body: some View {
NavigationView {
VStack {
Map(pins: $pins, selectedPin: $selectedPin)
.frame(width: 300, height: 300)
if selectedPin != nil {
Text(verbatim: "Welcome to \(selectedPin?.title ?? "???")!")
}
}
}
}
}
... и попробуйте zoomiнг / постукивание по булавке в Лондоне, Великобритания:)