'How to add polylines and annotations from user taps in SwiftUI

I am trying to drop annotations/pins wherever a user touches a certain location on the map. Whenever a user drops more 2 or more pins, it creates a polyline that connects the points. I got it to work in regular Swift 5, but I am trying to make it work in SwiftUI. I am using Mapbox. I am having trouble figuring out how to make it work with the coordinator. Can anyone help me figure this out? Thanks!


import SwiftUI
import Mapbox

// Creates an annotation using title & coordinate
extension MGLPointAnnotation {
    convenience init(title: String, coordinate: CLLocationCoordinate2D) {
        self.init()
        self.title = title
        self.coordinate = coordinate
    }
}


// Represents & Displays an MGLMapView in SwiftUI
struct MapView: UIViewRepresentable {

    // Property binding to add annotations
    @Binding var annotations: [MGLPointAnnotation]
    //var mapView: MGLMapView!
    //var coordinates = [CLLocationCoordinate2D]()

    
    // Creates a mapView with MGLMapView type
    private let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: MGLStyle.streetsStyleURL)

    // Needed function for UIViewRepresentable
    func makeUIView(context: UIViewRepresentableContext<MapView>) -> MGLMapView {
        mapView.delegate = context.coordinator
        
        // Add a single tap gesture recognizer. This gesture requires the built-in MGLMapView tap gestures (such as those for zoom and annotation selection) to fail.
        let singleTap = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleMapTap(sender:)))
        for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer {
            singleTap.require(toFail: recognizer)
        }
        mapView.addGestureRecognizer(singleTap)
         
        // Convert `mapView.centerCoordinate` (CLLocationCoordinate2D) to screen location (CGPoint).
        let centerScreenPoint: CGPoint = mapView.convert(mapView.centerCoordinate, toPointTo: nil)
        print("Screen center: \(centerScreenPoint) = \(mapView.center)")
        
        return mapView
    }
    
    // Needed function for UIViewRepresentable
    func updateUIView(_ uiView: MGLMapView, context: UIViewRepresentableContext<MapView>) {
            updateAnnotations()
            uiView.addAnnotations(annotations)
    }
    
    // Styles the map with a MapBox Studio URL
    func styleURL(_ styleURL: URL) -> MapView {
            mapView.styleURL = styleURL
            return self
    }
        
    // Specifies where the map is centered
    func centerCoordinate(_ centerCoordinate: CLLocationCoordinate2D) -> MapView {
            mapView.centerCoordinate = centerCoordinate
            return self
    }
    
    // Specifies the zoom level of the initial view
    func zoomLevel(_ zoomLevel: Double) -> MapView {
            mapView.zoomLevel = zoomLevel
            return self
    }
    
    // Updates the annotations in the view
    private func updateAnnotations() {
        if let currentAnnotations = mapView.annotations {
            mapView.removeAnnotations(currentAnnotations)
        }
        mapView.addAnnotations(annotations)
    }
    
    // Makes the coordinater (coordinator class below)
    func makeCoordinator() -> MapView.Coordinator {
        Coordinator(self, mapView)
    }
    
    // A coordinator used with a delegate to add the annotation view to the map
    // A coordinator class is declared to implement and view MGLMapViewDelegate in SwiftUI
    final class Coordinator: NSObject, MGLMapViewDelegate {
        var control: MapView
        var mapView: MGLMapView!
        var coordinates = [CLLocationCoordinate2D]()
        var pointAnnotations = [MGLPointAnnotation]()


        init(_ control: MapView, _ mapView: MGLMapView) {
            self.control = control
            self.mapView = mapView
        }
        
        
        func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
            return true
        }
        
        func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
            return nil
        }
        
        @objc func handleMapTap(sender: UITapGestureRecognizer) {
            // Convert tap location (CGPoint) to geographic coordinate (CLLocationCoordinate2D).
            let tapPoint: CGPoint = sender.location(in: mapView)
            let tapCoordinate: CLLocationCoordinate2D = mapView.convert(tapPoint, toCoordinateFrom: mapView)
            print("You tapped at: \(tapCoordinate.latitude), \(tapCoordinate.longitude)")
            
            // Create an array of coordinates for our polyline, starting at the center of the map and ending at the tap coordinate.
            //var coordinates: [CLLocationCoordinate2D] = [mapView.centerCoordinate]
            coordinates.append(tapCoordinate)
            print("Coordinates list: \(coordinates)")

            
            //var pointAnnotations = [MGLPointAnnotation]()
            for coordinate in coordinates {
                let point = MGLPointAnnotation()
                point.coordinate = coordinate
                point.title = "\(coordinate.latitude), \(coordinate.longitude)"
                pointAnnotations.append(point)
                
                print("Annotations: \(pointAnnotations)")
                
                let polyline = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count))
                mapView.addAnnotation(polyline)
            
            }
            mapView.addAnnotations(pointAnnotations)
             
            // Remove any existing polyline(s) from the map.
            //if mapView.annotations?.count != nil, let existingAnnotations = mapView.annotations {
                //mapView.removeAnnotations(existingAnnotations)
            //}
             
            // Add a polyline with the new coordinates.
            //let polyline = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count))
            //mapView.addAnnotation(polyline)
        }
    }
}


Solution 1:[1]

I faced with the same issue with you. I updated my library to the newest version and every thing worked!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Huu Toan Vo