I am struggling to try and Highlight the road between 2 points using MapKit. All I don't want directions i just want to try and Highlight the road between these two points.
My code is:
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
struct Points {
var name: String
var lattitude: CLLocationDegrees
var longtitude: CLLocationDegrees
}
override func viewDidLoad() {
super.viewDidLoad()
let points = [
Points(name: "Point 1", lattitude: 52.100525, longtitude: -9.623071),
Points(name: "Point 2", lattitude: 52.07241, longtitude: -9.575299)
]
fetchPointsOnMap(points)
}
func fetchPointsOnMap(_ points: [Points]) {
for points in points {
let annotations = MKPointAnnotation()
annotations.title = points.name
annotations.coordinate = CLLocationCoordinate2D(latitude:
points.lattitude, longitude: points.longtitude)
mapView.addAnnotation(annotations)
}
}
}`
You can draw a line:
directionsRequest.transportType = MKDirectionsTransportType.automobile
//Draw polyline by using MKRoute so it follows the street roads...
for (k, item) in arrayarrayPlacemarks.enumerated() {
if k < (arrayarrayPlacemarks.count - 1) {
directionsRequest.source = item
directionsRequest.destination = arrayarrayPlacemarks[k+1]
let directions = MKDirections(request: directionsRequest)
directions.calculate { (response:MKDirections.Response!, error: Error!) -> Void in
if error == nil {
self.locRoute = response.routes[0] as? MKRoute
let geodesic:MKPolyline = self.locRoute!.polyline
self.mapView.addOverlay(geodesic)
}
}
}
}
Delegate:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay.isKind(of: MKPolyline.self){
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.fillColor = UIColor.blue
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.lineWidth = 2
return polylineRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
Related
we are writing a code for navigation at swift. We implemented local database and downloaded our data into the app for offline navigation. But we have a problem. When we drawing road, its connecting beginning and endpoints
override func viewDidLoad() {
super.viewDidLoad()
CizimVeriler();
}
func CizimVeriler(){
var annotationCizim = getMapCizim()
var polyline2 : MKPolyline = MKPolyline()
var points: [CLLocationCoordinate2D] = [CLLocationCoordinate2D]()
for annotation in annotationCizim {
points.append(annotation.coordinate)
}
polyline2 = MKPolyline(coordinates: &points, count: points.count)
mapView.add(polyline2)
}
func getMapCizim() -> [Station] {
var annotationCizim:Array = [Station]()
annotationCizim.removeAll()
var stationCizim: NSArray?
do {
let secim = UserDefaults.standard.value(forKey: "RotaId") as! Int;
let filtrele = try self.database.prepare(self.RotaKoordinatTable.where(RotaKoorId == secim))
for rota in filtrele {
let x1 = Double(rota[(self.KoorX)])
let y1 = Double(rota[(self.KoorY)])
let annotation = Station(latitude: y1!, longitude: x1!)
annotation.title = "S1" as? String
annotationCizim.append(annotation)
}
} catch {
print(error)
}
return annotationCizim
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let polyLineRenderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
polyLineRenderer.strokeColor = #colorLiteral(red: 0.8961519599, green: 0.1830670238, blue: 0.06986772269, alpha: 1)
polyLineRenderer.lineWidth = 1
return polyLineRenderer
}
return MKOverlayRenderer()
}
I want to give name to the green and right pin annotation above.
I see a video tutorial, and he can give name to the annotation by using annotation.title = but I don't know why I can get the name correctly show in my MapKit.
here is the code I use
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapKit: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapKit.delegate = self
let bakrieTowerCoordinate = CLLocation(latitude: -6.23860724759536, longitude: 106.789429759178)
let GBKCoordinate = CLLocation(latitude: -6.23864960081552, longitude: 106.789627819772)
let locationGBK : CLLocationCoordinate2D = CLLocationCoordinate2DMake(-6.23864960081552, 106.789627819772)
let locationBakrieToweer : CLLocationCoordinate2D = CLLocationCoordinate2DMake(-6.23860724759536, 106.789429759178)
let annotation = MKPointAnnotation()
annotation.coordinate = locationGBK
annotation.title = "GBK"
annotation.subtitle = "Stadion"
mapKit.addAnnotation(annotation)
let annotation2 = MKPointAnnotation()
annotation2.coordinate = locationBakrieToweer
annotation2.title = "Bakrie Tower"
annotation2.subtitle = "Office"
mapKit.addAnnotation(annotation2)
zoomMapOn(location1: GBKCoordinate, location2: bakrieTowerCoordinate)
}
func zoomMapOn(location1: CLLocation, location2: CLLocation) {
let distanceOf2CoordinateInMeters = location1.distance(from: location2)
let radius = distanceOf2CoordinateInMeters * 3
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location1.coordinate, radius, radius)
mapKit.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
guard let locationName = annotation.title else {return nil}
if locationName == "GBK" {
annotationView.pinTintColor = UIColor.green
} else if locationName == "Bakrie Tower" {
annotationView.pinTintColor = UIColor.red
}
return annotationView
}
}
Add this code to your view controller -
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapKit: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapKit.delegate = self
let bakrieTowerCoordinate = CLLocation(latitude: -6.23860724759536, longitude: 106.789429759178)
let GBKCoordinate = CLLocation(latitude: -6.23864960081552, longitude: 106.789627819772)
let locationGBK : CLLocationCoordinate2D = CLLocationCoordinate2DMake(-6.23864960081552, 106.789627819772)
let locationBakrieToweer : CLLocationCoordinate2D = CLLocationCoordinate2DMake(-6.23860724759536, 106.789429759178)
let annotation = MKPointAnnotation()
annotation.coordinate = locationGBK
annotation.title = "GBK"
annotation.subtitle = "Stadion"
mapKit.addAnnotation(annotation)
let annotation2 = MKPointAnnotation()
annotation2.coordinate = locationBakrieToweer
annotation2.title = "Bakrie Tower"
annotation2.subtitle = "Office"
mapKit.addAnnotation(annotation2)
zoomMapOn(location1: GBKCoordinate, location2: bakrieTowerCoordinate)
}
func zoomMapOn(location1: CLLocation, location2: CLLocation) {
let distanceOf2CoordinateInMeters = location1.distance(from: location2)
let radius = distanceOf2CoordinateInMeters * 3
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location1.coordinate, radius, radius)
mapKit.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
guard let locationName = annotation.title else {return nil}
if locationName == "GBK" {
annotationView.canShowCallout = true
} else if locationName == "Bakrie Tower" {
annotationView.pinTintColor = UIColor.red
}
annotationView.canShowCallout = true // Add this line in your code
return annotationView
}
}
When you tap on the pin, it will show the text, Like -
Just Add
annotationView.canShowCallout = true inside your mapView(_ mapView:). Thank you.
You need to set this property in mapView(_:viewFor:) before returning your annotationView:
annotationView.canShowCallout = true
Now when you tap the pin, it will show your text.
When I add the annotations to the map they sometimes show and sometimes not depending on how close they are to each other. If they are in the same house lets say one won't show. How do I make both of them show? Do I need to make a custom annotation class? I heard ios11 has a clumping feature, do I need to use that? Here is the code(abridged):
import UIKit
import MapKit
import Firebase
class GameViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
fileprivate var locations = [CLLocation]()
fileprivate var userLocations = [(loc: CLLocation, name: String, team: String)]()
fileprivate var userAnnotations = [MKAnnotation]()
fileprivate var hasBeenUP = false
var ref: FIRDatabaseReference!
let uid = FIRAuth.auth()!.currentUser!.uid
var timer = Timer()
var timeLeft = 0.0
var firstTimer = Timer()
var name = ""
var team = ""
override func viewDidLoad() {
super.viewDidLoad()
let center = CLLocationCoordinate2D(latitude: 47.786769, longitude: -20.413634)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.mapView.setRegion(region, animated: true)
mapView.mapType = .hybrid
locationManager.startUpdatingLocation()
ref = FIRDatabase.database().reference()
setupULSending()
getMetaInfo()
ref.child("realtimeLocations").observe(FIRDataEventType.value, with: { (snapshot) in
self.userLocations = []
for rest in snapshot.children.allObjects as! [FIRDataSnapshot] {
guard let snapshotValue = snapshot.value as? NSDictionary, let snapVal = snapshotValue[rest.key] as? NSDictionary else {
break
}
let name = snapVal["name"] as! String
let team = snapVal["team"] as? String ?? ""
if let lat = snapVal["lat"] as? Double,
let long = snapVal["long"] as? Double {
let location = CLLocation(latitude: lat, longitude: long)
self.userLocations.append((loc: location, name: name, team: team))
}else {
}
}
DispatchQueue.main.async {
self.updateUserLocation()
}
})
}
private lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.allowsBackgroundLocationUpdates = true
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}()
func updateUserLocation() {
for an in self.mapView.annotations {
mapView.removeAnnotation(an)
}
for loc in userLocations {
let annotation = MKPointAnnotation()
annotation.coordinate = loc.loc.coordinate
annotation.title = loc.name
annotation.subtitle = "local"
mapView.addAnnotation(annotation)
}
}
}
// MARK: - CLLocationManagerDelegate
extension GameViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations
locations: [CLLocation]) {
let location = locations.last as! CLLocation
self.locations.append(location)
}
}
On the MKAnnotationView, you have to set the MKFeatureDisplayPriority to 'required'. You can modify the annotation views by implementing MKMapViewDelegate and mapView(MKMapView, viewFor: MKAnnotation). Something like this:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "yourIdentifier")
if view == nil {
view = MKMarkerAnnotationView(annotation: nil, reuseIdentifier: "yourIdentifier")
}
view?.displayPriority = .required
return view
}
More options for this are explained in the WWDC 2017 video 237 "What's New in MapKit"
For the practice of MapKit in iOS using Swift 2.0 targeted version iOS 8.0 drawing path between two points I am getting errors in my code that you can see above.
Here is my practice code:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
var geocoder:CLGeocoder = CLGeocoder()
var location:CLLocation = CLLocation(latitude: 38, longitude: -77)
var next:CLLocation = CLLocation(latitude: 38.21, longitude: -77.21)
var locMark:MKPlacemark?
var destMark:MKPlacemark?
var manager:CLLocationManager = CLLocationManager()
var source:MKMapItem?
var destination:MKMapItem?
var request:MKDirectionsRequest = MKDirectionsRequest()
var directions:MKDirections = MKDirections()
var directionsResponse:MKDirectionsResponse = MKDirectionsResponse()
var route:MKRoute = MKRoute()
override func viewDidLoad() {
super.viewDidLoad()
manager.requestAlwaysAuthorization()
map.mapType = MKMapType.Satellite
locMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude), addressDictionary: nil)
destMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(next.coordinate.latitude, next.coordinate.longitude), addressDictionary: nil)
source = MKMapItem(placemark: locMark)
destination = MKMapItem(placemark: destMark)
request.setSource(source)
request.setDestination(destination)
request.transportType = MKDirectionsTransportType.Automobile
request.requestsAlternateRoutes = true
directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler { (response:MKDirectionsResponse?, error:NSError?) -> Void in
if error == nil {
self.directionsResponse = response!
self.route = self.directionsResponse.routes[0] as! MKRoute
map.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads)
} else {
println(error)
}
}
}
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;
}
}
Zeeshan you just need to delete those two lines and add the two lines from Mark Answer , this below code is working fine:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
var geocoder:CLGeocoder = CLGeocoder()
var location:CLLocation = CLLocation(latitude: 38, longitude: -77)
var next:CLLocation = CLLocation(latitude: 38.21, longitude: -77.21)
var locMark:MKPlacemark?
var destMark:MKPlacemark?
var manager:CLLocationManager = CLLocationManager()
var source:MKMapItem?
var destination:MKMapItem?
var request:MKDirectionsRequest = MKDirectionsRequest()
var directions:MKDirections = MKDirections()
var directionsResponse:MKDirectionsResponse = MKDirectionsResponse()
var route:MKRoute = MKRoute()
override func viewDidLoad() {
super.viewDidLoad()
manager.requestAlwaysAuthorization()
map.mapType = MKMapType.Satellite
locMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude), addressDictionary: nil)
destMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(next.coordinate.latitude, next.coordinate.longitude), addressDictionary: nil)
source = MKMapItem(placemark: locMark)
destination = MKMapItem(placemark: destMark)
request.source = source
request.destination = destination
request.transportType = MKDirectionsTransportType.Automobile
request.requestsAlternateRoutes = true
directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler { (response:MKDirectionsResponse?, error:NSError?) -> Void in
if error == nil {
self.directionsResponse = response!
self.route = self.directionsResponse.routes[0] as! MKRoute
map.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads)
} else {
print(error)
}
}
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
print("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! {
print("ViewForOverlay")
if (overlay.isKindOfClass(MKPolyline)) {
var lineView: MKPolylineView = MKPolylineView(overlay: overlay)
lineView.backgroundColor = Colors.psnGreen
return lineView;
}
return nil;
}
}
Try changing the two lines to this:
request.source = source
request.destination = destination
Swift uses the property directly, rather than an explicit setter (which is only available in Obj-C).
I want to add a overlay path among multiple coordinates in mapview. I tried like below code, but it shows a error of "cannot invoke 'map' with an argument list of type ((CLLocation) -> CLLocationCoordinate2D)". Please let me know how can i fix this ?
My ViewController.swift file
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate{
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
//For Location 1
let location1 = CLLocationCoordinate2D(
latitude: 51.481188400000010000,
longitude: -0.190209099999947280
)
let annotation1 = MKPointAnnotation()
annotation1.coordinate = location1;
annotation1.title = "Chelsea"
annotation1.subtitle = "Chelsea"
let span = MKCoordinateSpanMake(0.15, 0.15)
let region1 = MKCoordinateRegion(center: location1, span: span)
mapView.setRegion(region1, animated: true)
mapView.addAnnotation(annotation1)
//For Location 2
let location2 = CLLocationCoordinate2D(
latitude: 51.554947700000010000,
longitude: -0.108558899999934510
)
let annotation2 = MKPointAnnotation()
annotation2.coordinate = location2;
annotation2.title = "Arsenal"
annotation2.subtitle = "Arsenal"
let region2 = MKCoordinateRegion(center: location1, span: span)
mapView.setRegion(region2, animated: true)
mapView.addAnnotation(annotation2)
var locations = [CLLocation(latitude: 51.481188400000010000, longitude: -0.190209099999947280), CLLocation(latitude: 51.554947700000010000,longitude: -0.108558899999934510)]
//This line shows error
var coordinates = locations.map({(location: CLLocation) -> CLLocationCoordinate2D in return location.coordinate})
var polyline = MKPolyline(coordinates: &coordinates, count: locations.count)
mapView.addOverlay(polyline)
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blueColor()
polylineRenderer.lineWidth = 5
return polylineRenderer
}
return nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This should work:
var coordinates = locations.map {
location in
return location.coordinate
}
One-liner:
var coordinates = locations.map { $0.coordinate }
The problem with your code was that locations is a variable of type [CLLocation!] (note the exclamation mark here), but you are declaring its elements as CLLocation (without the !) in the closure:
(location: CLLocation) -> CLLocationCoordinate2D