Mapbox Maps SDK for iOS - Maps won't display anymore (or gets cut off) - swift

Maps (from MapBox API) were working just fine in our app, but about a month they stopped displaying on a swiftui view. BUT the tricky thing is that the maps in some phones still work (or works partially but being cut off), and they still show the annotations/pins (see screenshot).
We have the MapBox Maps SDK for iOS installed as cocoapods into Xcode.
It's a simple app, it just detects a vehicle trip and add event points to the map (eg. point A as trip start, and point B as trip end).
Versions:
Xcode: 13.2.1
pod 'Mapbox-iOS-SDK', '~> 6.4.1'
pod 'MapboxNavigation', '~> 1.4.2'
Basically, the following is our main code to show the map.
Note: It also adds speeding, hard brake and rapid accelaration annotations to the map.
Does someone have any idea why the map is not showing properly?
Thanks much in advance.
import SwiftUI
import Mapbox
import MapboxDirections
import Polyline
struct TestMapDriveView: View {
var selectedTrip: VehicleTripData // The selected trip that needs to be shown
#Binding var speedingCount: Int? // the number of speeding events in the selected trip
#Binding var showCallouts: Bool // Whether to show callout when a point annotation is selected
#Binding var showInfoSheet: Bool // whether show the event details view or not (show the details when the user tap on a specific marker on the map)
#Binding var isFullScreen: Bool // whether show full screen map or not
#Binding var selectedEncodedPolyline: String // The encoded polyline for the trip
#State var alreadyClean: Bool = false
#State var changed: Bool = false
#State var showSpecificPoint = false // Whether focues on a specific trip point or not
let action: () -> () // Used to toggle the full screen state variable
var body: some View {
VStack {
NavigationLink(destination: EmptyView(), label: {EmptyView()})
NavigationLink(destination: EventDetails(tripPoint: selectedTripPoint, selectedTrip: selectedTrip, typeInfo: .constant(selectedTypeInfo))
, isActive: $showInfoSheet, label: {EmptyView()}).navigationTitle("").navigationBarTitle("").navigationBarHidden(true)
MapboxMap(selectedTrip: selectedTrip, speedingCount: $speedingCount, showCallouts: $showCallouts, showInfoSheet: $showInfoSheet, isFullScreen: $isFullScreen, selectedEncodedPolyline: $selectedEncodedPolyline, alreadyClean: $alreadyClean, changed: $changed, showSpecificPoint: $showSpecificPoint)
.onAppear {
showDetails = false
// The reason for adding a timer here is that since showDetails is a global variable,
// and it is not a state variable, we canot trigger its value
// We need a timer so that we can check the value of the showDetails every 0.1 second
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
showInfoSheet = showDetails
if showInfoSheet {
isFullScreen = false
findTripPoint()
timer.invalidate()
}
}
}
}
}
}
// MARK: - New Map Provided by MapBox
// MARK: - Since MapBox is designed to be implemented with the UIView, for SwiftUI, we need to transfer the UIView to View
struct MapboxMap: UIViewRepresentable {
var selectedTrip: VehicleTripData
#Binding var speedingCount: Int?
#Binding var showCallouts: Bool
#Binding var showInfoSheet: Bool
#Binding var isFullScreen: Bool
#Binding var selectedEncodedPolyline: String
#Binding var alreadyClean: Bool
#Binding var changed: Bool
#Binding var showSpecificPoint: Bool
private let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: MGLStyle.streetsStyleURL)
class Coordinator: NSObject, MGLMapViewDelegate {
var showCallouts: Bool
init(showcallouts: Bool) {
self.showCallouts = showcallouts
}
func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool {
return annotation.title != nil && annotation.title != "Speeding"
}
// When you tap on a point on the map, whether show the callout or not
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
if let title = annotation.title {
return (!(title == "Speeding") && showCallouts)
}
else {
return false
}
}
// MARK: - This function is used to replace the default marker icon with our icons for specific events
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
guard annotation is MGLPointAnnotation else {
return nil
}
guard annotation.title != nil else {return nil}
guard annotation.title! != nil else {return nil}
if annotation.title != ""{
let identifier = annotation.title!!
print("error \(annotation.title)")
var image = UIImage()
if identifier.hasPrefix("Speeding") {
image = UIImage(named: "Speeding Marker")!
} else if identifier.hasPrefix("Hard"){
image = UIImage(named: "Hard Brake Pin")!
} else if identifier.hasPrefix("Rapid") {
image = UIImage(named: "Rapid Accel Pin")!
} else {
image = UIImage(named: identifier)!
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MGLAnnotationView(annotation: annotation, reuseIdentifier: identifier as! String)
let imageView = UIImageView(image: image)
annotationView!.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
annotationView?.addSubview(imageView)
annotationView?.centerOffset = CGVector(dx: 0, dy: -image.size.height / 2.0)
}
return annotationView
}
return nil
}
// MARK: - This function is executed when the user taps on "View Details"
// In fact, we should set showInfoSheet to be true in order to show the EventDetailView
// However, since we are using a coordinator here, we cannnot change showInfoSheet directly
// Therefore, we change the global variable "showDetails" to be true
#objc func labelAction(_ sender: UITapGestureRecognizer) {
showDetails = true
}
// MARK: - Add the "View Details" Label in the Callout View
// MARK: - We should use UIButtion, however, if we use UIButtion, it will never be shown. Need further tests
func mapView(_ mapView: MGLMapView, leftCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
guard let title = annotation.title else {return nil}
if title != nil {
if title!.hasPrefix("Speeding:") {
selectedTypeInfo = "speeding"
} else if title!.hasPrefix("Hard") {
selectedTypeInfo = "hardBrake"
} else if title!.hasPrefix("Rapid") {
selectedTypeInfo = "rapidAccel"
} else {
return nil
}
let labelTap = UITapGestureRecognizer(target: self, action: #selector(labelAction(_:)))
labelTap.numberOfTapsRequired = 1
// Callout height is fixed; width expands to fit its content.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 60, height: 50))
label.textAlignment = .center
label.textColor = UIColor(named: "Blue")
label.text = "View Details"
label.numberOfLines = 2
label.font = UIFont(name: "NotoSans-Regular", size: 12)
label.isUserInteractionEnabled = true
label.addGestureRecognizer(labelTap)
selectedCoor = annotation.coordinate
return label
}
return nil
}
// MARK: - This function is used to change the color of the polyline based on the event name
func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor {
guard let title = annotation.title else {return UIColor(named: "Blue")!}
if title.hasPrefix("Speeding") {
return UIColor(named: "MapRed")!
} else {
return UIColor(named: "Blue")!
}
}
}
func makeCoordinator() -> MapboxMap.Coordinator {
return Coordinator(showcallouts: showCallouts)
}
// MARK: - Initialize the MapView
func makeUIView(context: Context) -> MGLMapView {
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: selectedTrip.startedLatitude!, longitude: selectedTrip.startedLongitude!), animated: true)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ uiView: MGLMapView, context: Context) {
if (uiView.annotations ?? []).count == 0 {
mapViewDidFinishLoadingMap(uiView, didFinishLoading: MGLStyle())
}
}
func mapViewDidFinishLoadingMap(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
mapView.isRotateEnabled = false
// MARK: - Get the result from the encoded polyline string
let result = Polyline.init(encodedPolyline: selectedEncodedPolyline, encodedLevels: .none)
// MARK: - Get the coordinates in the polyline for the trip
let coordinates = result.coordinates
guard let tripPoints = coordinates else {return}
// MARK: - Get the start and end point
let startPoint: CLLocationCoordinate2D = coordinates?.first ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
let endPoint: CLLocationCoordinate2D = coordinates?.last ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
var startTripPoint = MGLPointAnnotation()
var endTripPoint = MGLPointAnnotation()
startTripPoint.title = "Starting Point"
startTripPoint.coordinate = startPoint
endTripPoint.title = "End Point"
endTripPoint.coordinate = endPoint
mapView.addAnnotation(startTripPoint)
mapView.addAnnotation(endTripPoint)
let tryLine = MGLPolyline(coordinates: coordinates!, count: UInt(coordinates!.count))
mapView.addAnnotation(tryLine)
var speedingArray = [[TripPoint]]()
DispatchQueue.global(qos: .background).async {
// MARK: - Deal with the speeding events in the trip
// MARK: - The speeding icon will be placed at the point which has the maximum speed for each speeding polyline
var ctr = 0
while ctr < selectedTrip.speedingPoints!.count{
var speedPath = [TripPoint]()
speedPath.append(selectedTrip.speedingPoints![ctr])
while ctr + 1 < selectedTrip.speedingPoints!.count && selectedTrip.speedingPoints![ctr + 1].timeStamp!.timeIntervalSince1970 - selectedTrip.speedingPoints![ctr].timeStamp!.timeIntervalSince1970 < 10{
speedPath.append(selectedTrip.speedingPoints![ctr+1])
ctr += 1
}
speedingArray.append(speedPath)
ctr += 1
}
for speedingLine in speedingArray {
var maxDelta = 0.0
var maxDeltaPoint:TripPoint? = nil
var path: [CLLocationCoordinate2D] = []
for speedingPoint in speedingLine{
path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
if speedingPoint.speed_limit_delta! > maxDelta{
maxDelta = speedingPoint.speed_limit_delta!
maxDeltaPoint = speedingPoint
}
}
if let markerPoint = maxDeltaPoint{
tripPointsArray.append(markerPoint)
var speedMarker = MGLPointAnnotation()
speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
DispatchQueue.main.async {
mapView.addAnnotation(speedMarker)
}
}
let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
speedingPolyline.title = "Speeding"
DispatchQueue.main.async {
mapView.addAnnotation(speedingPolyline)
}
}
speedingCount = speedingArray.count
// MARK: - Deal with hard brakes in the trip
for hardBrakePoint in selectedTrip.hardBrakePoints!{
tripPointsArray.append(hardBrakePoint)
let hardBrakeMarker = MGLPointAnnotation()
hardBrakeMarker.coordinate = CLLocationCoordinate2D(latitude: hardBrakePoint.latitude!, longitude: hardBrakePoint.longitude!)
hardBrakeMarker.title = "Hard Brake"
hardBrakeMarker.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
DispatchQueue.main.async {
mapView.addAnnotation(hardBrakeMarker)
}
}
// MARK: - Deal with rapid accel in the trip
for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
tripPointsArray.append(rapidAccelPoint)
let rapidAccelMarker = MGLPointAnnotation()
rapidAccelMarker.coordinate = CLLocationCoordinate2D(latitude: rapidAccelPoint.latitude!, longitude: rapidAccelPoint.longitude!)
rapidAccelMarker.title = "Rapid Accel"
rapidAccelMarker.subtitle = "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
DispatchQueue.main.async {
mapView.addAnnotation(rapidAccelMarker)
}
}
}
// MARK: - If we are not in EventDetailView, then the mapView.showAnnotations will help us to zoom the map to the proper level so that all the markers and annotations in the map will be shown
if !showSpecificPoint {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { timer in
guard let annotations = mapView.annotations else {return}
mapView.showAnnotations(annotations, edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
}
} else {
// MARK: - If we need to zoom into a specific point, we need to find which point it is among all the trip points and zoom into that specific point
var alreadyAdded = false
var point = MGLPointAnnotation()
point.coordinate = CLLocationCoordinate2D(latitude: selectedTripPoint.latitude ?? 0, longitude: selectedTripPoint.longitude ?? 0)
for hardBrakePoint in selectedTrip.hardBrakePoints! {
if hardBrakePoint.latitude == selectedTripPoint.latitude && hardBrakePoint.longitude == selectedTripPoint.longitude {
point.title = "Hard Brake"
point.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
mapView.addAnnotation(point)
alreadyAdded = true
break
}
}
if !alreadyAdded {
for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
if rapidAccelPoint.latitude == selectedTripPoint.latitude && rapidAccelPoint.longitude == selectedTripPoint.longitude {
point.title = "Rapid Accel"
point.title! += "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
mapView.addAnnotation(point)
alreadyAdded = true
break
}
}
}
if !alreadyAdded {
var presentPolyLine: MGLPolyline = MGLPolyline()
for speedingLine in speedingArray{
var maxDelta = 0.0
var maxDeltaPoint:TripPoint? = nil
var path: [CLLocationCoordinate2D] = []
for speedingPoint in speedingLine{
path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
if speedingPoint.speed_limit_delta! > maxDelta{
maxDelta = speedingPoint.speed_limit_delta!
maxDeltaPoint = speedingPoint
}
}
if let markerPoint = maxDeltaPoint{
if selectedTripPoint.latitude == markerPoint.latitude && selectedTripPoint.longitude == markerPoint.longitude {
let specificMarker = MGLPointAnnotation()
specificMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude ?? 0, longitude: markerPoint.longitude ?? 0)
var speedMarker = MGLPointAnnotation()
speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
point = speedMarker
DispatchQueue.main.async {
let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
speedingPolyline.title = "Speeding"
presentPolyLine = speedingPolyline
}
break
} else {
path.removeAll()
}
}
}
mapView.showAnnotations([point], edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
} else {
mapView.showAnnotations([point], edgePadding: .init(top: -10, left: -10, bottom: -10, right: -10), animated: true, completionHandler: nil)
}
}
}
}
I tried updating the pods from 6.3 to 6.4.1 but it didn't work.
I know MapBox has a new framework as of v6.4.1 called v10, but I haven't installed yet cause that would change the whole code.
I am expecting to see the map as it was displaying before this new bug. eg. see screenshot

Related

custom Google Maps blue dot in Swift

What I want to achieve is to customize google maps blue dot with an svg icon,
with the icon moving when location is changed.
what I am doing:
struct GoogleMapsView: UIViewRepresentable {
#ObservedObject var locationManager = LocationManager.shared
#State var myLocationDot = GMSMarker()
#Binding var nearbyDrivers: [NearbyDriver]
init(nearbyDrivers: Binding<[NearbyDriver]>){
self._nearbyDrivers = nearbyDrivers
myLocationDot.position = locationManager.userLocation!.coordinate
}
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: locationManager.userLocation!.coordinate.latitude, longitude: locationManager.userLocation!.coordinate.longitude, zoom: 15)
let mapView = GMSMapView(frame: CGRect.zero, camera: camera)
myLocationDot.position = CLLocationCoordinate2D(latitude: locationManager.userLocation!.coordinate.latitude, longitude: locationManager.userLocation!.coordinate.longitude)
let myLocationDotIcon = UIImage(named: "myLocationDotIcon.svg")
let myLocationDotIconImage = UIImageView(image: myLocationDotIcon!)
myLocationDotIconImage.frame = CGRect(x: 0, y: 0, width: 40, height: 50)
myLocationDot.iconView = myLocationDotIconImage
myLocationDot.zIndex = 1
myLocationDot.map = mapView
// MARK: - Load nearby drivers
if !nearbyDrivers.isEmpty {
for driver in nearbyDrivers {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: driver.location.lat, longitude: driver.location.long)
let locationIcon = UIImage(named: "carAnnotation.png")
let locationIconImage = UIImageView(image: locationIcon!)
locationIconImage.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
marker.iconView = locationIconImage
marker.zIndex = 0
marker.map = mapView
}
}
//MARK: - MapStyle
do {
if let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") {
mapView.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
} else {
NSLog("Unable to find style.json")
}
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
return mapView
}
func updateUIView(_ uiView: GMSMapView, context: Context) {
uiView.isMyLocationEnabled = false
}
func makeCoordinator() -> Coordinator {
Coordinator(myLocationDot: $myLocationDot, owner: self)
}
class Coordinator: NSObject, GMSMapViewDelegate, CLLocationManagerDelegate {
#ObservedObject var locationManager = LocationManager.shared
#Binding var myLocationDot: GMSMarker
let owner: GoogleMapsView
init(myLocationDot: Binding<GMSMarker>, owner: GoogleMapsView) {
self.owner = owner
_myLocationDot = myLocationDot
}
func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
mapView.animate(toLocation: locationManager.userLocation!.coordinate)
return true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
myLocationDot.position = locations.last!.coordinate
}
}
}
The idea is that I need to change the blue dot appearance but leave the functionalities as they need to be.
For some reason I can't find any course on advanced google maps customization. if you could refer a guy that does the thing, that would be awesome.

Remove annotation when slider moves - swift

i have a problem with annotations that i can't resolve. When you click on a UIButton, the #IBAction pressPlay function starts, which causes the slider on my map to start moving. The slider has the max value 0 and min -31, and the initial value is 0 and it starts to move only if the thumb is in position! = From 0 and moves every 1 second. This works correctly moves the slider.
#IBAction func pressPlay(_ sender: Any)
{
let calendar2 = Calendar.current
let today = Date()
var cnt = Int(sliderTime.value)
let play = UIImage(named: "play")
let pause = UIImage(named: "pause")
let format = DateFormatter()
playButton.setImage(play, for: .normal)
if control == true && Int(sliderTime.value) < 0
{ //mette in play
control = false
playButton.setImage(pause, for: .normal)
//removeSeismometers = true
if Int(sliderTime.value) < 0
{
timer = Timer.scheduledTimer(withTimeInterval: 1,repeats: true)
{ [self]t in //ogni secondo questo timer cambia il valore dell'alpha del pin che sta vibrando
if cnt < 0
{
cnt = Int(self.sliderTime.value)
self.sliderTime.value += 1
let newDate2 = calendar2.date(byAdding: .day, value: Int(self.sliderTime.value), to:today)! //sottraggo alla data attuale il vlaore dello slider per tornare indietro nel tempo
format.dateStyle = .medium // "MM/GG/AAAA"
self.labelTime.text = "\(format.string(from: newDate2))"
appo += 1
for i in mapEqAnnotation{
let str: String = i.eq.eventTime
let index = str.index(str.endIndex, offsetBy: -9)
let mySubstring = str[..<index]
nuovaData = calendario.date(byAdding: .day, value: Int(sliderTime.value), to:dataCorrente)!
let format = DateFormatter()
format.dateFormat = "yyyy-MM-dd"
let dataCntr = "\(format.string(from: nuovaData))"
if mySubstring == dataCntr{
printQuake(quake: i)
}else{
removeQuake(quake:i)
}
}
//printQuake(sliderValue: appo)
}else if cnt == 0{
//removeSeismometers = false
playButton.setImage(play, for: .normal)
timer!.invalidate()
}
}
}
}else if control == false && Int(sliderTime.value) < 0 {
playButton.setImage(play, for: .normal)
control = true
timer!.invalidate()
}
}
My problem is that every second the slider has to move when you click on the UIButton, and every second has to add an annotation to the map and remove it as soon as you move the slider again.
Everything works, except that when the slider scrolls, the annotations of the previous move do not disappear, but remain on the map
func printQuake(quake: MapEarthquakeAnnotation){
let q = MapEarthquakeAnnotation(eq:quake.eq)
mapView.addAnnotation(q)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MapEarthquakeAnnotation{
annotazioni.append(annotation)
let EQAnnotation = annotation as! MapEarthquakeAnnotation
var view: MKPinAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: EQAnnotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.brown
return view
}else if (annotation is MapSeismometerAnnotation) {
if let annotation = annotation as? MapSeismometerAnnotation
{
var view: MKPinAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.green
view.image = UIImage(named: "pin-verde")
return view
}
return nil
}
return nil
}
Can you give me some advice?
It would be helpful to see the code you use in removeQuake but after a quick view of your code a very likely candidate is the code
func printQuake(quake: MapEarthquakeAnnotation){
let q = MapEarthquakeAnnotation(eq:quake.eq)
mapView.addAnnotation(q)
}
Here you create a new annotation each time you call this method. And this annotation is not preserved anywhere. So when you call removeQuake(quake:i) you can not expect that i is any of the annotations you have added using printQuake.
I am not sure why this code was build the it was but it is possible all you need to do is change this method to
func printQuake(quake: MapEarthquakeAnnotation){
mapView.addAnnotation(quake)
}
In general your code is a bit hard to read. You should look into more modern approaches using Swift and you should try to split parts of code so that they are easier to read and maintain. Non-English languages are not very helpful either.
I tried to tear down and reconstruct your code. Not everything is there and I am not sure it does what you want it to do. But still please inspect it. Maybe it will help you fix the issue you have.
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet private var mapView: MKMapView!
#IBOutlet private var sliderTime: UISlider!
#IBOutlet private var playButton: UIButton!
#IBOutlet private var labelTime: UILabel!
private var timer: Timer?
private var earthquakeAnnotations: [MapEarthquakeAnnotation] = []
private var dayOffset: Int = -30 {
didSet {
refresh()
}
}
private var isPlaying: Bool = false {
didSet {
playButton.setImage(isPlaying ? UIImage(named: "play") : UIImage(named: "pause"), for: .normal)
}
}
private func refresh() {
let beginningOfToday: Date = Calendar.autoupdatingCurrent.date(from: Calendar.autoupdatingCurrent.dateComponents([.year, .month, .day], from: Date()))!
let presentedDay = Calendar.autoupdatingCurrent.date(byAdding: .day, value: dayOffset, to: beginningOfToday)!
refreshSlider()
refreshTimeLabel(date: presentedDay)
refreshAnnotations(date: presentedDay)
}
private func refreshSlider() {
sliderTime.value = Float(dayOffset)
}
private func refreshTimeLabel(date: Date) {
labelTime.text = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter.string(from: date)
}()
}
private func refreshAnnotations(date: Date) {
earthquakeAnnotations.forEach { annotation in
if annotation.eq.date == date {
if !mapView.annotations.contains(where: { $0 === annotation }) {
mapView.addAnnotation(annotation)
}
} else {
if mapView.annotations.contains(where: { $0 === annotation }) {
mapView.removeAnnotation(annotation)
}
}
}
}
private func stopPlaying() {
timer?.invalidate()
timer = nil
isPlaying = false
}
private func startPlaying() {
if dayOffset < 0 {
isPlaying = true
timer?.invalidate() // Just in case
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
if self.dayOffset < 0 {
self.dayOffset += 1
} else {
self.stopPlaying()
}
})
} else {
// Already at the end. Should restart?
}
}
#IBAction func pressPausePlay(_ sender: Any) {
if isPlaying {
stopPlaying()
} else {
startPlaying()
}
}
}
// MARK: - MKMapViewDelegate
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? MapEarthquakeAnnotation {
let view: MKPinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.brown
return view
} else if let annotation = annotation as? MapSeismometerAnnotation {
let view: MKPinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.green
view.image = UIImage(named: "pin-verde")
return view
} else {
return nil
}
}
}
// MARK: - MapEarthquakeAnnotation
private extension ViewController {
class MapEarthquakeAnnotation: NSObject, MKAnnotation {
var identifier: String { "MapEarthquakeAnnotationID" }
let coordinate: CLLocationCoordinate2D
let eq: Earthquake
init(earthquake: Earthquake, coordinate: CLLocationCoordinate2D) { self.eq = earthquake; self.coordinate = coordinate }
}
}
// MARK: - MapSeismometerAnnotation
private extension ViewController {
class MapSeismometerAnnotation: NSObject, MKAnnotation {
var identifier: String { "MapSeismometerAnnotationID" }
let coordinate: CLLocationCoordinate2D
init(coordinate: CLLocationCoordinate2D) { self.coordinate = coordinate }
}
}
// MARK: - Earthquake
private extension ViewController {
struct Earthquake {
let eventTime: String
var date: Date {
let index = eventTime.index(eventTime.endIndex, offsetBy: -9)
let format = DateFormatter()
format.dateFormat = "yyyy-MM-dd"
return format.date(from: String(eventTime[..<index])) ?? Date()
}
}
}

Google Map with Cluster and Custom view marker lagging too much while zoomIn and zoomOut

I want to load GoogleMap with custom view in replace of GMSMarker and want to show clustering and I have done that. But I'm facing lagging & memory usage while zoomIn or ZoomOut the Map.
I have load custom view to GMSMarker infoView
Here is my code.
class InitialMapViewController: UIViewController, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
private var clusterManager: GMUClusterManager!
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.initializeClusterItems()
self. setMapView()
}
//MARK:- Map view actions
func setMapView() {
mapView.delegate = self
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
mapView.mapType = .normal
self.mapView.camera = self.defaultCamera(latitude: SharedData.sharedInstance.userLocation.coordinate.latitude, longitude: SharedData.sharedInstance.userLocation.coordinate.longitude)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.getAllFriendsList()
}
//MARK: INITIALIZE CLUSTER ITEMS
func initializeClusterItems() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUGridBasedClusterAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
guard let _ = marker.userData as? POIItem else { return }
if UIImage(named: "profile_circle_gradiant") != nil {
marker.iconView = UIImageView(image: nil, highlightedImage: nil)
}
}
private func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
}
func defaultCamera(latitude: Double, longitude: Double) -> GMSCameraPosition {
let camera = GMSCameraPosition.camera(withLatitude: latitude,
longitude: longitude,
zoom: 16)
return camera
}
func setMarkers() {
for i in 0..<SharedData.sharedInstance.allFriends.count {
let marker = GMSMarker()
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
//marker.icon = imgTypeDoctor
marker.accessibilityHint = String(i)
let infoWindow = Bundle.main.loadNibNamed("HomeMapInfoView", owner: self, options: nil)?.first as! HomeMapInfoView
//infoWindow.imgUser.sd_setImage(with: URL(string: friend.user_details.user_photo), placeholderImage: #imageLiteral(resourceName: "User_profile"), options: .highPriority, completed: nil)
infoWindow.imgCar.image = UIImage.init(named: "small_" + friend.user_details.car_personality_name)
infoWindow.imgCar1.image = UIImage.init(named: "small_" + friend.user_details.car_personality_name)
// infoWindow.lblName.text = friend.user_details.name
// infoWindow.lblUsername.text = "#" + friend.user_details.screen_name
//KULDEEP 01/03/2019
infoWindow.btnImgVW.tag = Int(marker.accessibilityHint!) ?? 0
infoWindow.isUserInteractionEnabled = false
infoWindow.btnImgVW.addTarget(self, action: #selector(btnUserTapped(_:)), for: .touchUpInside)
marker.iconView = infoWindow
marker.tracksViewChanges = true
//marker.userData = friend
marker.map = mapView
//mapView.selectedMarker = marker
//print(i)
self.generatePOIItems(String(format: "%d", i), position: marker.position, icon: nil)
}
self.clusterManager.cluster()
//self.clusterManager.setDelegate(self, mapDelegate: self)
}
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
UIView.animate(withDuration: 5.0, animations: { () -> Void in
//self.londonView?.tintColor = .blue
}, completion: {(finished) in
// Stop tracking view changes to allow CPU to idle.
//mapView.selectedMarker?.tracksViewChanges = false
})
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
let marker = GMSMarker()
if object is POIItem {
// set image view for gmsmarker
}
return marker
}
func generatePOIItems(_ accessibilityLabel: String, position: CLLocationCoordinate2D, icon: UIImage?) {
let item = POIItem(position: position, name: accessibilityLabel)
self.clusterManager.add(item)
}
}
Please guide me why map is lagging when I zoomIn or zoomOut the Map or what am I missing on this code?
Here I fix the map lagging issue and for that I have done the following things.
Instead of loading all marker iconView at the starting, I load it inside willRenderMarker so it initially load the 1 or 2 marker iconView and when I zoomin the map clustering expand and marker iconView displays.
Here is the working code.
class InitialMapViewController: UIViewController, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
private var clusterManager: GMUClusterManager!
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.initializeClusterItems()
self. setMapView()
}
//MARK:- Map view actions
func setMapView() {
mapView.delegate = self
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
mapView.mapType = .normal
self.mapView.camera = self.defaultCamera(latitude: SharedData.sharedInstance.userLocation.coordinate.latitude, longitude: SharedData.sharedInstance.userLocation.coordinate.longitude)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.getAllFriendsList()
}
//MARK: INITIALIZE CLUSTER ITEMS
func initializeClusterItems() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUGridBasedClusterAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
marker.groundAnchor = CGPoint(x: 0.5, y: 1)
if let markerData = (marker.userData as? POIItem) {
let infoWindow = Bundle.main.loadNibNamed("InitialMapInfoView", owner: self, options: nil)?.first as! InitialMapInfoView
infoWindow.imgUser.sd_setImage(with: URL(string: markerData.friend.user_details.user_photo_small), placeholderImage: #imageLiteral(resourceName: "User_profile"), options: .highPriority, completed: nil)
infoWindow.imgCar.image = UIImage.init(named: "small_" + markerData.friend.user_details.car_personality_name)
infoWindow.lblName.text = markerData.friend.user_details.name
infoWindow.btnImgVW.tag = markerData.userIndex
infoWindow.btnImgVW.addTarget(self, action: #selector(btnUserTapped(_:)), for: .touchUpInside)
marker.accessibilityHint = String(markerData.userIndex)
marker.iconView = infoWindow
marker.tracksViewChanges = false
}
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}
func defaultCamera(latitude: Double, longitude: Double) -> GMSCameraPosition {
let camera = GMSCameraPosition.camera(withLatitude: latitude,
longitude: longitude,
zoom: 16)
return camera
}
func setMarkers() {
for i in 0..<SharedData.sharedInstance.allFriends.count {
let marker = GMSMarker()
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
marker.accessibilityHint = String(i)
marker.icon = nil
marker.tracksViewChanges = true
marker.map = mapView
self.generatePOIItems(String(format: "%d", i), position: marker.position, icon: nil, friend: friend, userIndex: i)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
func generatePOIItems(_ accessibilityLabel: String, position: CLLocationCoordinate2D, icon: UIImage?) {
let item = POIItem(position: position, name: accessibilityLabel)
self.clusterManager.add(item)
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if marker.accessibilityHint != nil {
let friend = SharedData.sharedInstance.allFriends[Int(marker.accessibilityHint!) ?? 0]
objSelectedUser = friend
self.setUserData(friend: objSelectedUser)
self.showUserLatestBeeppView()
}
return true
}
}

Swift 4.2 : How to select/deselect multiple marker on google map

I'm implementing multiple markers on google map using swift in iOS. I've successfully placed all the markers on the map and can select the marker using 'didTap marker'method. But I'm unable to deselect it. When user select on one marker than the rest of the markers should be deselected.
extension MapVC {
// Mark:- Create Marker and set position
fileprivate func setMarkerOnMap() {
if self.markerArray.count != 0 {
for i in 0...self.markerArray.count - 1 {
let data = self.markerArray[i]
guard let lat = data.lat else {
return
}
guard let lon = data.long else {
return
}
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: Double(lat)!, longitude: Double(lon)!, zoom: zoomLevel)
showMarker(position: camera.target, index: i)
}
}
}
// Mark:- Show marker on map
fileprivate func showMarker(position: CLLocationCoordinate2D, inde x: Int) {
let marker = GMSMarker()
marker.position = position
marker.icon = UIImage(named: "marker-unselected-icon")
marker.accessibilityLabel = "\(index)" // get index from array when click on each marker to identify
marker.map = self.mapView
}
}
//MARK - Map
extension MapVC: GMSMapViewDelegate {
// MARK:- DidTap marker
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker)-> Bool {
if let markerLbl = marker.accessibilityLabel{
index = Int(markerLbl)
}
if marker != self.userMarker{
marker.icon = UIImage(named: "marker-selected-icon")
self.restaurantDetailView.isHidden = false
self.distanceView.isHidden = true
self.infoView.isHidden = true
}
var isReturn = true
mapView.selectedMarker = nil
if self.markerArray.count != 0 {
for i in 0...markerArray.count - 1 {
if i == index {
let storeInfo = markerArray[i]
storeInfo.isSelected = !storeInfo.isSelected
self.destinationLat = Double(storeInfo.lat!)!
self.destinationLng = Double(storeInfo.long!)!
marker.icon = UIImage(named: "marker-selected-icon")
markerArray[i] = storeInfo
} else {
marker.icon = UIImage(named: "marker-unselected-icon")
let storeInfo = markerArray[i]
storeInfo.isSelected = false
markerArray[i] = storeInfo
}
}
} else {
mapView.selectedMarker = nil
isReturn = true
return isReturn
}
return isReturn
}
}
I'm changing the selected marker image in did tap method but unfortunately, it is not working. I don't know what I'm doing wrong. Thanks in Advance.
When user want to select one marker and want to deselect all the other markers then following code would be usefull.
To Deselect all Marker on Map:
for (marker in self.markerArray) {
self.mapview.selectedMarker = nil
}
To Select Marker on Map:
self.mapView.selectedMarker = self.userMarker

how to make the annotation move over the polyline

i have 3 annotation and i draw polyline between first and second annotation but i need the therd one move over that polyline but it's always move in street polyline to the destnation
-my code
func moveDelivery(_ destinationCoordinate : CLLocationCoordinate2D{
self.deliveryAnnotation.coordinate = CLLocationCoordinate2DMake(29.959640, 31.270421)
let sourcePlaceMark = MKPlacemark(coordinate: self.userAnnotation.coordinate)
//sourcePlaceMark.title
let destPlaceMkark = MKPlacemark(coordinate: self.deliveryAnnotation.coordinate)
let sourceItem = MKMapItem(placemark: sourcePlaceMark)
let destItem = MKMapItem(placemark: destPlaceMkark)
let directionRequest = MKDirections.Request()
directionRequest.source = sourceItem
directionRequest.destination = destItem
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: {
response, error in
guard let response = response else {
if let error = error {
print(error.localizedDescription)
} else {
self.deliveryAnnotation.courseDegrees = self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
}
return
}
guard let primaryRoute = response.routes.first else { return }
let route = response.routes[0]
self.mapView.addOverlay(route.polyline, level: .aboveRoads)
let rekt = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)
})
//
UIView.animate(withDuration: Double(60), animations: {
self.deliveryAnnotation.coordinate = destinationCoordinate
}, completion: { success in
if success {
}
})
}
Your third annotation isn't following the route because you're animating it moving in a straight line between the first and second line. Try getting the coordinates from the MKRoute's polyline and animate between each one (According to apple's docs MKRoutes are made up of coordinates, but you might be able to use points as well)
If you'd like it to animate over the span of 60 seconds:
func moveDelivery(_ destinationCoordinate: CLLocationCoordinate2D) {
// I don't know why you have the delivery annotation start here, is this for testing?
deliveryAnnotation.coordinate = CLLocationCoordinate2DMake(29.959640, 31.270421)
let sourcePlaceMark = MKPlacemark(coordinate: destinationCoordinate)
let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: {
response, error in
guard let response = response else {
print("MKRequest gave no response")
if let error = error {
print(error.localizedDescription)
} else {
self.deliveryAnnotation.courseDegrees = self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
}
return
}
guard let primaryRoute = response.routes.first else {
print("response has no routes")
return
}
self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)
let rekt = primaryRoute.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)
let coordinateArray = primaryRoute.polyline.coordinates
assert(coordinateArray.count > 0, "coordinate array is empty")
self.routeCoordinates = coordinateArray
// initiate recursive animations
self.coordinateIndex = 0
})
}
var routeCoordinates = [CLLocationCoordinate2D]()
var avgAnimationTime: Double {
return 60 / Double(routeCoordinates.count)
}
var coordinateIndex: Int! {
didSet {
guard coordinateIndex != routeCoordinates.count else {
print("animated through all coordinates, stopping function")
return
}
animateToNextCoordinate()
}
}
func animateToNextCoordinate() {
let coordinate = routeCoordinates[coordinateIndex]
UIView.animate(withDuration: avgAnimationTime, animations: {
self.deliveryAnnotation.coordinate = coordinate
}, completion: { _ in
self.coordinateIndex += 1
print("moved between coordinates")
})
}
EDIT
make sure to include this extension, otherwise you won't be able to get the coordinates of the MKRoute (source: https://gist.github.com/freak4pc/98c813d8adb8feb8aee3a11d2da1373f)
public extension MKMultiPoint {
var coordinates: [CLLocationCoordinate2D] {
var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
count: pointCount)
getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))
return coords
}
}
EDIT #2
See above, edited original answer to animate through each coordinate after the previous finishes animating. Really rough but it should work.
EDIT #3
Added your code to get the destination variable as well as some assert and debug printing calls. If things aren't working this time, please tell me which debug messages you get.
EDIT #4
I just demo'd my code and it works. Here is the MapViewController class I used along with necessary extensions:
private let reuseId = "deliveryReuseId"
private let userTitle = "user"
private let startingPointTitle = "store"
private let deliveryTitle = "delivery truck"
class MapViewController: UIViewController {
var mapView: MKMapView!
// annotations for this demo, replace with your own annotations
var deliveryAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = deliveryTitle
return annotation
}()
let userAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = userTitle
annotation.coordinate = CLLocationCoordinate2DMake(29.956694, 31.276854)
return annotation
}()
let startingPointAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = startingPointTitle
annotation.coordinate = CLLocationCoordinate2DMake(29.959622, 31.270363)
return annotation
}()
override func viewDidLoad() {
super.viewDidLoad()
loadMapView()
navigate()
}
func loadMapView() {
// set map
mapView = MKMapView()
view = mapView
mapView.delegate = self
mapView.register(MKAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseId)
// add annotations
mapView.addAnnotation(userAnnotation)
mapView.addAnnotation(startingPointAnnotation)
mapView.addAnnotation(deliveryAnnotation)
}
func navigate() {
let sourcePlaceMark = MKPlacemark(coordinate: startingPointAnnotation.coordinate)
let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: { response, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let primaryRoute = response!.routes.first else {
print("response has no routes")
return
}
self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)
self.mapView.setRegion(MKCoordinateRegion(primaryRoute.polyline.boundingMapRect), animated: true)
// initiate recursive animation
self.routeCoordinates = primaryRoute.polyline.coordinates
self.coordinateIndex = 0
})
}
var routeCoordinates = [CLLocationCoordinate2D]()
var avgAnimationTime: Double {
// to show delivery in 60 second, replace 60 with amount of seconds you'd like to show
return 60 / Double(routeCoordinates.count)
}
var coordinateIndex: Int! {
didSet {
guard coordinateIndex != routeCoordinates.count else {
print("animated through all coordinates, stopping function")
return
}
animateToNextCoordinate()
}
}
func animateToNextCoordinate() {
let coordinate = routeCoordinates[coordinateIndex]
UIView.animate(withDuration: avgAnimationTime, animations: {
self.deliveryAnnotation.coordinate = coordinate
}, completion: { _ in
self.coordinateIndex += 1
})
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
// replace these images with your own
switch annotation.title {
case userTitle:
annotationView.image = UIImage(named: "user")
case startingPointTitle:
annotationView.image = UIImage(named: "store")
case deliveryTitle:
annotationView.image = UIImage(named: "deliveryTruck")
default: break
}
return annotationView
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard overlay is MKPolyline else {
return MKOverlayRenderer()
}
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = .black
renderer.lineWidth = 5
renderer.lineJoin = .round
return renderer
}
}
public extension MKMultiPoint {
var coordinates: [CLLocationCoordinate2D] {
var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
count: pointCount)
getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))
return coords
}
}