'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 |