How to use SwiftUI to display a map and handle touch on the map?

I want to use SwiftUI to display a map and handle touch on the map. Here is my code so far. I have a problem integrating GestureRecognizer in Coordinator specifically accessing self.mapView inside it.


Also adding addGestureRecognizer. How to do that?

=======================================

//

// ContentView.swift


import SwiftUI

import Gomobiletidb


struct ContentView: View {


var body: some View {

VStack {

MapView()

.edgesIgnoringSafeArea(.vertical)


Text("Turtle Rock")

.font(.title)

.padding(.leading, 15)

.padding(.top, 5)

.textFieldStyle(RoundedBorderTextFieldStyle())

VStack {

HStack {

Text("Joshua Tree National Park")

.font(.subheadline)

Spacer()

Text("California")

.font(.subheadline)

}


.padding()

}


}

}

=======================================


//

// MapView.swift


import SwiftUI

import MapKit


struct MapView: UIViewRepresentable {

func makeCoordinator() -> Coordinator {

Coordinator(self)

}

final class Coordinator: NSObject, MKMapViewDelegate {

var control: MapView

init(_ control: MapView) {

self.control = control

longPressGestureRecognizer = UILongPressGestureRecognizer(target: self.control, action: #selector(Coordinator.mapViewLongTapRecognizer(gestureRecognizer:)))

control.addGestureRecognizer(longPressGestureRecognizer)

}

@objc func mapViewLongTapRecognizer( gestureRecognizer: UILongPressGestureRecognizer) {

if (gestureRecognizer == longPressGestureRecognizer){

if (gestureRecognizer.numberOfTouchesRequired == 1){

if gestureRecognizer.state == UIGestureRecognizer.State.began {

let touchPoint: CGPoint = gestureRecognizer.location(in: self.mapView)

}

}

}

}

// MARK: - MKMapViewDelegate

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {

let renderer = MKOverlayPathRenderer()

return renderer

}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

return annotation as? MKAnnotationView

}

}


func makeUIView(context: Context) -> MKMapView {

let mapView = MKMapView(frame: .zero)

mapView.delegate = context.coordinator

mapView.mapType = .hybrid

mapView.showsCompass = true

mapView.showsUserLocation = true

mapView.showsBuildings = true

return mapView

}



func updateUIView(_ mapView: MKMapView, context: Context) {

longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(Coordinator.mapViewLongTapRecognizer(gestureRecognizer:)))

mapView.addGestureRecognizer(longPressGestureRecognizer)


let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)

let region = MKCoordinateRegion(center: currentLocation.coordinate, span: span)

mapView.setRegion(region, animated: true)

}


}


struct MapView_Preview: PreviewProvider {

static var previews: some View {

MapView()

}

}

=======================================

Replies

After working some time on this problem, I think I came up with a solution in Swift 5 after looking at this stack owerflow article: iOS : Swift - How to add pinpoint to map on touch and get detailed address of that location?
First, you create a subclass of MKMapView:

Code Block
import Foundation
import MapKit
final class WrappedMap: MKMapView {
var onLongPress: (CLLocationCoordinate2D) -> Void = { _ in }
init() {
super.init(frame: .zero)
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
gestureRecognizer.minimumPressDuration = 0.8
addGestureRecognizer(gestureRecognizer)
}
@objc func handleTap(sender: UILongPressGestureRecognizer) {
if sender.state == .began {
let location = sender.location(in: self)
let coordinate = convert(location, toCoordinateFrom: self)
onLongPress(coordinate)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}


Now, you use this view instead of the normal MKMapView but you still have all the functionallity because you have MKMapView as the super class.

So you add this to your swiftUI code of the MapView:

Code Block
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
@State private var annotation = MKPointAnnotation()
func makeUIView(context: Context) -> MKMapView {
let mapView = WrappedMap()
mapView.delegate = context.coordinator
mapView.onLongPress = addAnnotation(for:)
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.removeAnnotations(uiView.annotations)
uiView.addAnnotation(annotation)
}
func addAnnotation(for coordinate: CLLocationCoordinate2D) {
let newAnnotation = MKPointAnnotation()
newAnnotation.coordinate = coordinate
annotation = newAnnotation
}
}
extension MapView {
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
.edgesIgnoringSafeArea(.all)
}
}


The Coordinator isn't used in this example, but you might need it when you use this view.
I hope this solutions works for your problem altough it has been something like a year since you asked.