Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you let a user add a pin to a map and get the coordinates in SwiftUI?

I have a map in a view that is centered on Chicago. I want the user to be able to place a pin/annotation on the map and then retrieve those coordinates. The map loads Chicago fine but I can't get the annotation code to work.

I can't seem to find an answer for SwiftUI, specifically. Only Swift and Storyboards. I feel like I have 99% of the code but the pieces aren't in the right spots. I included a screen shot of where the errors are. Thanks for your help.

import SwiftUI
import MapKit

struct EntireMapView: UIViewRepresentable {

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

        mapView.delegate = context.coordinator

        let longPressGesture = UILongPressGestureRecognizer(target: mapView, action: #selector(EntireMapViewCoordinator.addAnnotation(gesture:)))
        mapView.addGestureRecognizer(longPressGesture)

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

        var chicagoCoordinate = CLLocationCoordinate2D()
        chicagoCoordinate.latitude = 41.878113
        chicagoCoordinate.longitude = -87.629799

        let region = MKCoordinateRegion(center: chicagoCoordinate, span: span)

        mapView.setRegion(region, animated: true)

    }

    func makeUIView(context: Context) -> MKMapView {

        let mapView = MKMapView(frame: .zero)
        mapView.delegate = context.coordinator
        return mapView

    }

    func makeCoordinator() -> EntireMapViewCoordinator {
        return EntireMapViewCoordinator(self)
    }

    class EntireMapViewCoordinator: NSObject, MKMapViewDelegate {

        var entireMapViewController: EntireMapView

        init(_ control: EntireMapView) {
          self.entireMapViewController = control
        }

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

        @objc func addAnnotation(gesture: UILongPressGestureRecognizer) {

            if gesture.state == .ended {

                let point = gesture.location(in: self.mapView) **<--- ERROR HERE**
                let coordinate = mapView.convert(point, toCoordinateFrom: mapView)

                var annotation = MKPointAnnotation()
                annotation.coordinate = coordinate

                self.mapView.addAnnotation(annotation) **<--- ERROR HERE**
            }
        }
    }
}

struct EntireMapView_Previews: PreviewProvider {
    static var previews: some View {
        EntireMapView()
    }
}

Here's a screen shot of the errors

like image 399
kbeeveer46 Avatar asked Dec 04 '19 18:12

kbeeveer46


People also ask

How do I show a map in SwiftUI?

Displaying a Map View in SwiftUI To work with Map , you need to provide a binding of MKCoordinateRegion that keeps track of the region to display on the map. The MKCoordinateRegion structure lets you specify a rectangular geographic region centered around a specific latitude and longitude.

What is MapKit in iOS?

MapKit is a powerful API available on iOS devices that makes it easy to display maps, mark locations, enhance with custom data and even draw routes or other shapes on top.

What is Mkcoordinatespan in Swift?

The width and height of a map region.


2 Answers

Here is a complete code for the answer. The credit goes mostly to @yeezy , because obtaining view with this line: let mapView = gestureRecognizer.view as? MKMApView was the main mistake in the question.

  struct EntireMapView: UIViewRepresentable {

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

            let span = MKCoordinateSpan(latitudeDelta: 0.3, longitudeDelta: 0.3)
            var chicagoCoordinate = CLLocationCoordinate2D()
            chicagoCoordinate.latitude = 41.878113
            chicagoCoordinate.longitude = -87.629799
            let region = MKCoordinateRegion(center: chicagoCoordinate, span: span)
            mapView.setRegion(region, animated: true)

        }

        func makeUIView(context: Context) -> MKMapView {

            let myMap = MKMapView(frame: .zero)
            let longPress = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(EntireMapViewCoordinator.addAnnotation(gesture:)))
            longPress.minimumPressDuration = 1
            myMap.addGestureRecognizer(longPress)
            myMap.delegate = context.coordinator
            return myMap

        }

    func makeCoordinator() -> EntireMapViewCoordinator {
        return EntireMapViewCoordinator(self)
    }

    class EntireMapViewCoordinator: NSObject, MKMapViewDelegate {

        var entireMapViewController: EntireMapView

        init(_ control: EntireMapView) {
          self.entireMapViewController = control
        }


        @objc func addAnnotation(gesture: UIGestureRecognizer) {

            if gesture.state == .ended {

                if let mapView = gesture.view as? MKMapView {
                let point = gesture.location(in: mapView)
                let coordinate = mapView.convert(point, toCoordinateFrom: mapView)
                let annotation = MKPointAnnotation()
                annotation.coordinate = coordinate
                mapView.addAnnotation(annotation)
                }
            }
        }
    }
}
like image 179
0ndre_ Avatar answered Sep 21 '22 03:09

0ndre_


i don't know if you are still looking for an answer. But here is one. I struggled with this problem a lot and i don't know if my answer is correct but it works for me so here it is:

@objc func addAnnotation(gesture: UITapGestureRecognizer){
   if let mapView = gestureRecognizer.view as? MKMApView{
      let point = gestureRecognizer.location(in: mapView) 
      let coordinate = mapView.convert(point, toCoordinateFrom: mapView) 
      let annotation = MKPointAnnotation() 
      annotation.coordinate = coordinate 
      DispatchQueue.main.async{
          mapView.addAnnotation(annotation)
        }
    }
}

as you can see i am not using an UILongPressGestureRecognizer but rather an UITapGestureRecognize and i took your idea of using the gestureRecognizers view.

Disclaimer: I don't know if this is the best way to do it! I just started swift/swiftUI programming half a year ago :) and this is my first answer.

For further implementation: You should prolly just collect all the annotations in an array and update the mapView from within the updateUIView method. I dont know if i am correct when using the DispatchQueue.main.async here. But whenever i come across changes of the Ui i think you should do it on the main thread

Screenshot with explanation

like image 43
yeezy Avatar answered Sep 19 '22 03:09

yeezy