I am trying to write an app that will show the route of a tram line. I have added the map, but I am having problems trying to find how to add this route to my app.
I have looked at MKOverlayRenderer and I think I have to add an image on top of the map to do this. There are some tutorials but they are outdated.
Can someone help me with this. Thank you
As a beginner you should check the https://www.raywenderlich.com/90971/introduction-mapkit-swift-tutorial.
example
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer! {
if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.redColor()
lineView.lineWidth = 1
return lineView
}
return nil
}
func addRoute() {
mapView.deselectAnnotation(selectedAnnotationView.annotation, animated: true)
let track = Track.GetAll()// to get list of coordinates you should write your own way to store
if track.count == 0 {
return
}
var pointsToUse: [CLLocationCoordinate2D] = []
var isTrackChanged = false
for i in 0...track.count-1 {
let x = CLLocationDegrees((track[i].Latitude as NSString).doubleValue)
let y = CLLocationDegrees((track[i].Longitude as NSString).doubleValue)
pointsToUse += [CLLocationCoordinate2DMake(x, y)]
if i > 0 {
if pointsToUse[i-1].latitude != pointsToUse[i].latitude || pointsToUse[i-1].longitude != pointsToUse[i].longitude {
isTrackChanged = true
}
}
}
let myPolyline = MKGeodesicPolyline(coordinates: &pointsToUse, count: track.count)
mapView.addOverlay(myPolyline)
}
//model
class Track{
var latitude =""
var longitude=""
}
Related
I have drawn straight line between locations using MKPolyline in MKMapView
let locations:[CLLocationCoordinate2D] = ...
let polyLine = MKPolyline(coordinates: locations, count: locations.count)
mapView.add(polyLine)
// MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 4.0
return renderer
}
Now I want show drawing animation of line from one coordinate to another instead of already drew lines. Is there any possibility to do that? Any idea will be appreciated.
From your array of coordinates draw a poly line between first item and the second say in 0.1 seconds , then draw with the first item to the third and so on until draw with the first item to the last one with a timer you can have animated draw from source to destination
An array of coordinates will be needed. If you have only beginning and end coordinates, get array of coordinates using below code
func getPointsOnRoute(from: CLLocation?, to: CLLocation?, on mapView: MKMapView?) -> [CLLocation]? {
let NUMBER_OF_PIXELS_TO_SKIP: Int = 120
//lower number will give a more smooth animation, but will result in more layers
var ret = [Any]()
var fromPoint: CGPoint? = nil
if let aCoordinate = from?.coordinate {
fromPoint = mapView?.convert(aCoordinate, toPointTo: mapView)
}
var toPoint: CGPoint? = nil
if let aCoordinate = to?.coordinate {
toPoint = mapView?.convert(aCoordinate, toPointTo: mapView)
}
let allPixels = getAllPoints(from: fromPoint!, to: toPoint!)
var i = 0
while i < (allPixels?.count)! {
let pointVal = allPixels![i] as? NSValue
ret.append(point(toLocation: mapView, from: (pointVal?.cgPointValue)!)!)
i += NUMBER_OF_PIXELS_TO_SKIP
}
ret.append(point(toLocation: mapView, from: toPoint!)!)
return ret as? [CLLocation] }
Having array of coordinates add rendering of the overlays in MKMapViewDelegate’s delegate method — mapView(_:rendererFor:).
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let polyline = overlay as? MKPolyline else {
return MKOverlayRenderer()
}
let polylineRenderer = MKPolylineRenderer(overlay: polyline)
polylineRenderer.strokeColor = .black
polylineRenderer.lineWidth = 2
return polylineRenderer
}
mapView.addOverlay(polyline) // add it to mapview
render the polyline in small segments to create the animation effect
var drawingTimer: Timer?
// .... // Somewhere in your View Controller
func animate(route: [CLLocationCoordinate2D], duration: TimeInterval, completion: (() -> Void)?) {
guard route.count > 0 else { return }
var currentStep = 1
let totalSteps = route.count
let stepDrawDuration = duration/TimeInterval(totalSteps)
var previousSegment: MKPolyline?
drawingTimer = Timer.scheduledTimer(withTimeInterval: stepDrawDuration, repeats: true) { [weak self] timer in
guard let self = self else {
// Invalidate animation if we can't retain self
timer.invalidate()
completion?()
return
}
if let previous = previousSegment {
// Remove last drawn segment if needed.
self.mapView.removeOverlay(previous)
previousSegment = nil
}
guard currentStep < totalSteps else {
// If this is the last animation step...
let finalPolyline = MKPolyline(coordinates: route, count: route.count)
self.mapView.addOverlay(finalPolyline)
// Assign the final polyline instance to the class property.
self.polyline = finalPolyline
timer.invalidate()
completion?()
return
}
// Animation step.
// The current segment to draw consists of a coordinate array from 0 to the 'currentStep' taken from the route.
let subCoordinates = Array(route.prefix(upTo: currentStep))
let currentSegment = MKPolyline(coordinates: subCoordinates, count: subCoordinates.count)
self.mapView.addOverlay(currentSegment)
previousSegment = currentSegment
currentStep += 1
}
}
I have downloaded tiles for a small town in zooms 15 to 18. They were displaying as expected but now don't.
I was using xcode 7 with swift 2 on Yosemite. I'm now with xcode 8.1 with swift 3 on sierra. I'm pretty sure the problem started before the upgrade, but have no time machine to go back. I upgraded because I wanted to test the app on my IPad 2, got the message that xcode wasn't compatible with the ios on the ipad, then got a message that xcode 8.1 cant be loaded to Yosemite hence upgrade to sierra.
The code is below. And the console output bellow that. No error given
//
// MapViewController.swift
// button scale
//
// Created by Colin McGarry on 24/03/16.
// Copyright © 2016 Colin McGarry. All rights reserved.
//
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController /*, MKMapViewDelegate*/{
var guideData: GuideData?
var dataM:[GuideData] = [GuideData]()
var placeNumb = 0
var modifyingMap = true
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let IS_RETINA = (UIScreen.mainScreen().respondsToSelector(#selector(UIScreen.displayLinkWithTarget(_:selector:))) && (UIScreen.mainScreen().scale >= 2.0))
// from shankar map
if IS_RETINA {
print("Is Retina")
} else {
print("not retina")
}
zoomToRegion()
let annotations = getMapAnnotations()
// Add mappoints to Map
mapView.addAnnotations(annotations)
// end shankar map
//Get the URL template to the map tiles
let baseURL = NSBundle.mainBundle().bundleURL.absoluteString
let urlTemplate = baseURL!.stringByAppendingString("osmm/{z}/{x}/{y}.png/")
//let urlTemplate = baseURL.stringByAppendingString("two/{z}/{x}/{y}#2x.png/")
//let urlTemplate = "http://tile.openstreetmap.org/{z}/{x}/{y}.png"
print(urlTemplate)
let carte_indice = MKTileOverlay(URLTemplate:urlTemplate)
carte_indice.geometryFlipped = false
carte_indice.canReplaceMapContent = true
//carte_indice.tileSize = CGSize(width: 512, height: 512)
carte_indice.maximumZ = 16
carte_indice.minimumZ = 18
self.mapView.addOverlay(carte_indice)
}
/// from shankar map
func zoomToRegion() {
let location = CLLocationCoordinate2D(latitude: 49.275, longitude: -0.7028)
let region = MKCoordinateRegionMakeWithDistance(location, 500.0, 500.0)
mapView.setRegion(region, animated: true)
}
//MARK:- Annotations
func getMapAnnotations() -> [Stands] {
var annotations:Array = [Stands]()
//load plist file
var stands: NSArray?
if let path = NSBundle.mainBundle().pathForResource("stands", ofType: "plist") {
stands = NSArray(contentsOfFile: path)
}
//iterate and create annotations
if let items = stands {
for item in items {
let lat = item.valueForKey("lat") as! Double
let long = item.valueForKey("long")as! Double
let annotation = Stands(latitude: lat, longitude: long)
let tit = item.valueForKey("title") as! String
let numb = item.valueForKey("no") as! Int
annotation.title = "\(numb) \(tit)"
annotation.no = numb
// new added
// annotation.enabled = true
// annotation.canShowCallOut = true
// end added
annotations.append(annotation)
}
}
return annotations
}
// end From Shankar map
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
// 1
let identifier = "Stand"
// 2
if annotation.isKindOfClass(Stands.self) {
// 3
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
//4
annotationView = MKPinAnnotationView(annotation:annotation, reuseIdentifier:identifier)
annotationView!.canShowCallout = true
// 5
let btn = UIButton(type: .DetailDisclosure)
annotationView!.rightCalloutAccessoryView = btn
} else {
// 6
annotationView!.annotation = annotation
}
return annotationView
}
// 7
return nil
}
func mapView( mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
let standM = view.annotation as! Stands
let placeName = standM.title
let placeInfo = standM.title
placeNumb = standM.no!
/*
let ac = UIAlertController(title: placeName, message: placeInfo, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil) */
performSegueWithIdentifier("MapToText", sender: self)
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// enforce maximum zoom level
let place = mapView.visibleMapRect
if( place.origin.x < 133686298
|| place.origin.x > 133697376
|| place.origin.y < 91864219
|| place.origin.y > 91874305
)
{
zoomToRegion()
}
print("alt \(mapView.camera.altitude)")
let maxAlt = 3000.00
if (mapView.camera.altitude > maxAlt && self.modifyingMap)
{
self.modifyingMap = false
// prevents strange infinite loop case
self.mapView.zoomEnabled = false
/* self.mapView.scrollEnabled = false
self.mapView.userInteractionEnabled = false
*/
print("place \(place)")
self.mapView.camera.altitude = maxAlt
self.modifyingMap = true
print("alt>\(maxAlt)")
} else {
self.mapView.zoomEnabled = true
print("place2 \(place)")
print("x \(place.origin.x)")
print(" alt less than \(maxAlt)")
print(mapView.camera.altitude)
}
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!
{
print("call overlay")
if overlay is MKTileOverlay
{
print("is MKTileoverlay")
let renderer = MKTileOverlayRenderer(overlay:overlay)
renderer.alpha = 0.8
return renderer
}
return nil
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "MapToText" {
let webviewController:webViewController = segue.destinationViewController as! webViewController
webviewController.index = placeNumb - 1
// webviewController.standTitle = sender as! MKAnnotationView
webviewController.inde = placeNumb - 1
print("prepSeg \(webviewController.inde)")
}
}
}
console output
/Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button scale.app/stands.plist
Optional("/Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button scale.app/2page.html")
Is Retina
alt 1100.94691259802
place2 MKMapRect(origin: __C.MKMapPoint(x: 133691120.58898854, y: 91870217.34526816), size: __C.MKMapSize(width: 5123.4971518665552, height: 6063.3100336045027))
x 133691120.588989
alt less than 3000.0
1100.94691259802
file:///Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button%20scale.app/osmm/{z}/{x}/{y}.png/
call overlay
is MKTileoverlay
I found the problem. But it's not in the code I included.
I had put min and max zoom. When I changed the values I interchanged min and max
overlay.maximumZ = 15
overlay.minimumZ = 17
These conditions are mutually contradicting so overlay didn't show.
if I'd written min above max I might have noticed before.
I want to get the directions between two points and draw them to my MKMapView. This is my implementation:
First, at my controller's viewDidLoad callback, I set map's view delegate to self
map.delegate = self
Then, I request all routes:
func showDirections() {
let currentPlace: MKPlacemark = MKPlacemark(coordinate: location.coordinate, addressDictionary: nil)
let place: MKPlacemark = MKPlacemark(coordinate: annotation.coordinate, addressDictionary: nil)
println(currentPlace.coordinate.latitude)
println(currentPlace.coordinate.longitude)
println(place.coordinate.latitude)
println(place.coordinate.longitude)
var request: MKDirectionsRequest = MKDirectionsRequest.new()
request.setSource(MKMapItem(placemark: currentPlace))
request.setDestination(MKMapItem(placemark: place))
request.transportType = MKDirectionsTransportType.Any
request.requestsAlternateRoutes = true
var directions: MKDirections = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler() {
(response, error) in
println(error)
if(error == nil && response != nil) {
println("Numero de rutas: " + String(response.routes.count))
println(response.routes[0].coordinate)
for(var i = 0;i < response.routes.count; i++) {
self.map.addOverlays(response.routes[i].overlays, level: MKOverlayLevel.AboveRoads)
self.map.setNeedsDisplay()
}
for r in response.routes { println("route = \(r)") }
}
}
}
And implement this two callbacks too:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
println("redenrerForOverlay")
if(overlay.isKindOfClass(MKPolyline)) {
var renderer: MKPolylineRenderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = Colors.psnGreen
renderer.lineWidth = 5
return renderer
}
return nil
}
func mapView(mapView: MKMapView!, viewForOverlay overlay: MKOverlay!) -> MKOverlayView! {
println("ViewForOverlay")
if (overlay.isKindOfClass(MKPolyline)) {
var lineView: MKPolylineView = MKPolylineView(overlay: overlay)
lineView.backgroundColor = Colors.psnGreen
return lineView;
}
return nil;
}
When i run my application, I get two routes to draw, but when I call map.addOverlay, nothing happens. My rendererForOverlay/viewForOverlay are never called. Somenone know how to make it work?
Thanks in advance
I finnaly got the solution
For those who are interested, only change the directions callback to this:
directions.calculateDirectionsWithCompletionHandler() {
(response, error) in
if(error == nil && response != nil) {
for route in response.routes {
var r: MKRoute = route as! MKRoute
self.map.addOverlay(r.polyline, level: MKOverlayLevel.AboveRoads)
}
}
}
I am currently working on adding cluster annotations to my map view. Everything is working fine with the exception of a couple of things. First of all, I need to refresh the annotations on the map when the user leaves and returns to the view. At the moment this sort of works... The problem is that rather than removing the annotations and adding the new ones, the new ones are just being added in addition of the old ones so there are multiplying each time.
The next issue is that each annotation displays the distance the user is from that annotation, however the annotations are set up before the user's location is found. I assume I will just have to remove and replace the annotations once the location is found, but then I run into the issue of the annotations duplicating again.
This is my code:
override func viewDidAppear(animated: Bool) {
println(rideArray.count)
setUpMapView()
}
func setUpMapView() {
rideArray = ((DataManager.sharedInstance.rideArray) as NSArray) as! [Ride]
myLocation = mapView.userLocation.coordinate as CLLocationCoordinate2D
zoomRegion = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2D(latitude: parkPassed.latitude!, longitude: parkPassed.longitude!), 1000, 1000)
mapView.setRegion(zoomRegion, animated: true)
mapView.delegate = self
for ride in rideArray {
println("Location: \(mapView.userLocation.coordinate.latitude)")
var subtitle = ""
if mapView.userLocation.location == nil {
subtitle = "Distance unavailable"
} else {
let userLocation = CLLocation(latitude: mapView.userLocation.coordinate.latitude, longitude: mapView.userLocation.coordinate.longitude)
let annotationLocation = CLLocation(latitude: ride.latitude!, longitude: ride.longitude!)
var distance = Int(CLLocationDistance(annotationLocation.distanceFromLocation(userLocation)))
if distance > 1000 {
distance = distance / 1000
subtitle = "\(distance) kilometers"
} else {
subtitle = "\(distance) meters"
}
}
let annotation = RideAnnotation(coordinate: CLLocationCoordinate2DMake(ride.latitude!, ride.longitude!), title: ride.name!, subtitle: subtitle)
self.qTree.insertObject(annotation)
}
var leftSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
var rightSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
leftSwipe.direction = .Left
rightSwipe.direction = .Right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation.isKindOfClass(QCluster.classForCoder()) {
let PinIdentifier = "PinIdentifier"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(ClusterAnnotationView.reuseId()) as? ClusterAnnotationView
if annotationView == nil {
annotationView = ClusterAnnotationView(cluster: annotation)
}
annotationView!.cluster = annotation
return annotationView
} else if annotation.isKindOfClass(RideAnnotation.classForCoder()) {
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier("RideAnnotation")
if pinView == nil {
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: "RideAnnotation")
pinView?.canShowCallout = true
pinView?.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton
pinView?.rightCalloutAccessoryView.tintColor = UIColorFromRGB(0x424242)
let rideTimeView = UIView()
rideTimeView.frame = CGRectMake(5, 5, 50, 50)
rideTimeView.layer.cornerRadius = 25
let waitTimeLabel = UILabel()
waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
rideTimeView.backgroundColor = getColorFromNumber(80)
waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
} else {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
}
waitTimeLabel.textAlignment = NSTextAlignment.Center
waitTimeLabel.textColor = UIColor.whiteColor()
waitTimeLabel.adjustsFontSizeToFitWidth = true
waitTimeLabel.numberOfLines = 1
rideTimeView.addSubview(waitTimeLabel)
pinView.leftCalloutAccessoryView = rideTimeView
pinView?.image = UIImage(named: "rideMapAnnotation")
} else {
pinView?.annotation = annotation
let rideTimeView = UIView()
rideTimeView.frame = CGRectMake(5, 5, 50, 50)
rideTimeView.layer.cornerRadius = 25
let waitTimeLabel = UILabel()
waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
rideTimeView.backgroundColor = getColorFromNumber(80)
waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
} else {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
}
waitTimeLabel.textAlignment = NSTextAlignment.Center
waitTimeLabel.textColor = UIColor.whiteColor()
waitTimeLabel.adjustsFontSizeToFitWidth = true
waitTimeLabel.numberOfLines = 1
rideTimeView.addSubview(waitTimeLabel)
pinView.leftCalloutAccessoryView = rideTimeView
}
return pinView
}
return nil
}
func reloadAnnotations(){
if self.isViewLoaded() == false {
return
}
self.cacheArray.removeAll(keepCapacity: false)
let mapRegion = self.mapView.region
let minNonClusteredSpan = min(mapRegion.span.latitudeDelta, mapRegion.span.longitudeDelta) / 5
let objects = self.qTree.getObjectsInRegion(mapRegion, minNonClusteredSpan: minNonClusteredSpan) as NSArray
for object in objects {
if object.isKindOfClass(QCluster){
let c = object as? QCluster
let neighbours = self.qTree.neighboursForLocation((c?.coordinate)!, limitCount: NSInteger((c?.objectsCount)!)) as NSArray
for neighbour in neighbours {
let tmp = self.rideArray.filter({
return $0.name == (neighbour.title)!!
})
if find(self.cacheArray, tmp[0]) == nil {
self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
}
}
} else {
let tmp = self.rideArray.filter({
return $0.name == (object.title)!!
})
if find(self.cacheArray, tmp[0]) == nil {
self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
}
}
}
let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
annotationsToRemove.removeObject(self.mapView.userLocation)
annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
let annotationsToAdd = objects.mutableCopy() as! NSMutableArray
annotationsToAdd.removeObjectsInArray(self.mapView.annotations)
self.mapView.addAnnotations(annotationsToAdd as [AnyObject])
}
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
viewChanged = true
self.reloadAnnotations()
}
I apologise for the extensive amount of code, but it all had to be included.
Anyone have any suggestions as to how I can remove the annotations and re-add them once the view appears?
Thanks!
EDIT:
The second issue is now resolved. Here is a bit more detail in regards to the situation and issue:
Basically each annotation represents a ride in a theme park and displays the ride name, current wait time and distance to that ride. At the moment since I am calling setUpMapView() when the view appears, all of the ride annotations are being added to the qTree each time, but are not being removed. This is the issue that I am trying to resolve, but I can't find a way to remove them from the qTree.
MKMapView has the method removeAnnotation:, which will remove a single annotation, and removeAnnotations: which will remove an array of annotations. It also has an annotations property which lets you the current array of annotations. You should be able to select and remove the annotations that you want to remove and then add new ones to replace them.
I think you could try finding user location like this
let locationManager: CLLocationManager = CLLocationManager()
(write this as a class property, not inside any viewDidLoad!)
then u instantly have locationManager coordinates.
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
could you print the "annotationsToRemove as [AnyObject]" and tell us what is the content ?
actually all this code is suspicious:
let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
annotationsToRemove.removeObject(self.mapView.userLocation)
annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
you define a constant, then edit it with removeObjects and then pass it to removeAnnotations ? strange man, please get as many prints on these lines as you can and tell what they are so we can help
I have my MKMapViewDelegate in place. Also, MapView.delegate = self
let c1 = myCLLocationCoodinate
let c2 = myCLLocationCoodinate2
var a = [c1, c2]
var polyline = MKPolyline(coordinates: &a, count: a.count)
self.MapView.addOverlay(polyline)
With this Delegate Method:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.whiteColor()
polylineRenderer.lineWidth = 2
return polylineRenderer
}
return nil
}
I get this: EXC BAD ACCESS Thread 8 on
self.MapView.addOverlay(polyline)
I think issue here is with the line:
var a = [c1, c2]
Here you directly created array without specifying its type.
See below reference code to create Polyline overlay and related delegate method:
let c1 = myCLLocationCoodinate
let c2 = myCLLocationCoodinate2
var points: [CLLocationCoordinate2D]
points = [c1, c2]
var geodesic = MKGeodesicPolyline(coordinates: &points[0], count: 2)
mapView.add(geodesic)
UIView.animate(withDuration: 1.5, animations: { () -> Void in
let span = MKCoordinateSpanMake(20, 20)
let region1 = MKCoordinateRegion(center: c1, span: span)
mapView.setRegion(region1, animated: true)
})
A delegate method to render overlay:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.whiteColor()
polylineRenderer.lineWidth = 2
return polylineRenderer
}
return nil
}
It seems that your map view has been deallocated. The polyline construction is OK.
Normally, variables start with lowercase. Have you subclassed the map view and are trying to access the class?
I spent WAAAAAAAAYYYY too much time on this so I thought I would add the solution to a similar issue. I was getting a EXC BAD ACCESS on addOverlay w/ a MKPolygon. Turns out I was just on the wrong thread the whole time. Fixed it with:
var points = [MKMapPoint]()
for var i = 0; i < area.coordinates.count; i+=2 {
let c = CLLocationCoordinate2DMake(area.coordinates[i], area.coordinates[i+1])
points.append(MKMapPointForCoordinate(c))
}
let polygon = MKPolygon(points: &points, count: points.count)
dispatch_async(dispatch_get_main_queue(), {
self.mapView.addOverlay(polygon)
})
let firstlat : string = "12.9166"
let firstlon : string = "77.6101"
let secondlat : string = "12.9610"
let secondLon : string = "77.6387"
let point1 = CLLocationCoordinate2DMake(Double(firstlat)!, Double(firstlon)!)
let point2 = CLLocationCoordinate2DMake(Double(secondlat as String)!, Double(secondLon)!)
let pickAnnotation : MKPointAnnotation = MKPointAnnotation()
pickAnnotation.coordinate = point1
pickAnnotation.title = "pick"
displayMapView.addAnnotation(pickAnnotation)
let dropAnnotation : MKPointAnnotation = MKPointAnnotation()
dropAnnotation.coordinate = point2
dropAnnotation.title = "drop"
displayMapView.addAnnotation(dropAnnotation)
displayMapView.showAnnotations(displayMapView.annotations, animated: true)
var points: [CLLocationCoordinate2D]
points = [point1, point2]
routeLine = MKPolyline(coordinates: &points[0] , count: 2)
displayMapView.add(routeLine)
func showRouteOnMap(_ pickCoordinate: CLLocationCoordinate2D, _ destinationCoordinate: CLLocationCoordinate2D) {
let request = MKDirections.Request()
let sourcePlacemark = MKPlacemark(coordinate: pickCoordinate)
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
request.source = sourceMapItem
let myPlacemark = MKPlacemark(coordinate: destinationCoordinate)
let destinationMapItem = MKMapItem(placemark: myPlacemark)
request.destination = destinationMapItem
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
directions.calculate(completionHandler: {(response, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let response = response {
self.showRoute(response)
}
}
})
}
func showRoute(_ response: MKDirections.Response) {
for route in response.routes {
routeMap.addOverlay(route.polyline,
level: MKOverlayLevel.aboveRoads)
self.routeMap.setVisibleMapRect(route.polyline.boundingMapRect, animated: true)
}
}
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor(red: 17.0/255.0, green: 147.0/255.0, blue: 255.0/255.0, alpha: 1)
renderer.lineWidth = 5.0
return renderer
}