Associating different pin images with different locations iOS - ios

I have an array of 4 locations, and an array of 4 pin names (ie "bluePin.png | redPin.png | etc...")
Currently I'm using a bit of code I found elsewhere to add custom pin graphics to all 4 locations, but all of my pins are red.
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let annotationReuseId = "Truck"
var trkPin = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationReuseId)
if trkPin == nil {
trkPin = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationReuseId)
} else {
trkPin!.annotation = annotation
}
trkPin!.image = UIImage(named: "redPin.png")
trkPin!.backgroundColor = UIColor.clearColor()
trkPin!.canShowCallout = false
return trkPin
}
How could I associate the pin colors with the locations in the array, so that each location is differentiated by color?
Thanks!

I have done this in the simple way by declaring array of location and array images. And I have viewDidLoad method as follows.
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
let location = [
CLLocationCoordinate2D(latitude: 12, longitude: 77),
CLLocationCoordinate2D(latitude: 12.5, longitude: 77.5),
CLLocationCoordinate2D(latitude: 13, longitude: 78),
CLLocationCoordinate2D(latitude: 13.5, longitude: 78)
]
let pins = [
"red.png",
"blue.png",
"green.png",
"yellow.png"
]
var annotations = [MKPointAnnotation]()
for (index, eachLocation) in location.enumerate() {
let pinImageName = pins[index]
let annotation = MKPointAnnotation()
annotation.coordinate = eachLocation
annotation.title = "\(pinImageName)"
annotations.append(annotation)
}
mapView.addAnnotations(annotations)
}
And in the viewForAnnotation delegate method just getting that image name from the title property and explicitly disabling the callout
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let annotationReuseId = "Truck"
var trkPin = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationReuseId)
if trkPin == nil {
trkPin = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationReuseId)
trkPin!.canShowCallout = false
} else {
trkPin!.annotation = annotation
if let image = annotation.title {
trkPin!.image = UIImage(named: image ?? "default.png")
} else {
trkPin!.image = UIImage(named: "default.png")
}
}
return trkPin
}

Related

My button I created progmatically to push from my app the Apple Maps app stopped working.. why?

I have a navigation app I am working on which works like a normal maps app, allowing you to search for locations through a search table etc. So in my app I have a button which allows for the user to add multiple annotations (be default every time the user clicks a new result in the searchtable I have it set to remove the most recent annotation) and find the centerpoint between all these areas. The code then shifts that centerpoint to the closest address so there is a place to get directions to, not just lat and long. Anyway, I have it set so that when the user taps the annotation, a button appears right above it, and the button is supposed to send you to apple maps with the preset route already loaded in. This worked until recently. Here is the code that carries this button's actions out:
extension ViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.pinTintColor = UIColor.red
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: (CGPoint()), size: smallSquare))
button.setBackgroundImage(UIImage(named: "car"), for: [])
button.addTarget(self, action: #selector(getDirections), for: UIControlEvents.touchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView
}
}
and in the class
#objc func getDirections(){
if let selectedPin = selectedPin {
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMaps(launchOptions: launchOptions)
}
}
Could it be something weird with the objective C function? This is the code I recently added that potentially could have caused the issue, though I really don't know:
func resolveAddress(for averageCoordinate: CLLocationCoordinate2D, completion: #escaping (MKPlacemark?) -> () ) {
let geocoder = CLGeocoder()
let averageLocation = CLLocation(latitude: averageCoordinate.latitude, longitude: averageCoordinate.longitude)
geocoder.reverseGeocodeLocation(averageLocation) { (placemarks, error) in
guard error == nil,
let placemark = placemarks?.first
else {
completion(nil)
return
}
completion(MKPlacemark(placemark: placemark ))
}
}
#IBAction func middleFinderButton(_ sender: Any) {
let totalLatitude = mapView.annotations.reduce(0) { $0 + $1.coordinate.latitude }
let totalLongitude = mapView.annotations.reduce(0) { $0 + $1.coordinate.longitude }
let averageLatitude = totalLatitude/Double(mapView.annotations.count)
let averageLongitude = totalLongitude/Double(mapView.annotations.count)
let centerPoint = MKPointAnnotation()
centerPoint.coordinate.latitude = averageLatitude
centerPoint.coordinate.longitude = averageLongitude
mapView.addAnnotation(centerPoint)
resolveAddress(for: centerPoint.coordinate) { placemark in
if let placemark = placemark {
self.mapView.addAnnotation(placemark)
} else {
self.mapView.addAnnotation(centerPoint)
}
}
print(totalLatitude)
print(totalLongitude)
print(averageLatitude)
print(averageLongitude)
print(centerPoint.coordinate)
}
}
Anyone have an idea why this button isn't working and pushing the app to maps with a preset route? could it be a glitch with the simulator(it used to work so I doubt it)? Thanks.

Swift adding annotations on map

Trying to make some annotations on the map. But can't get around it. the code I'm using is
// Names
names = ["Ben", "Big", "Hawk", "Enot", "Wiltons", "Scott's", "The Laughing"]
// Latitudes, Longitudes
coordinates = [
[51.519066, -0.135200],
[51.513446, -0.125787],
[51.465314, -0.214795],
[51.507747, -0.139134],
[51.509878, -0.150952],
[51.501041, -0.104098],
[51.485411, -0.162042],
[51.513117, -0.142319]
]
The function addAnnotation takes an array of type CLLocation, which is what you should use. So make the array look like this to initialize CLLocation objects. I assume you already have a working rendered map, mapView object, etc.
let coords = [ CLLocation(latitude: xxxx, longitude: xxxx),
CLLocation(latitude: xxx, longitude: xxx),
CLLocation(latitude: xxx, longitude:xxx)
];
here is a function that can take that array, and loops through each element and adds it as an annotation to the mapView (not yet rendered)
func addAnnotations(coords: [CLLocation]){
for coord in coords{
let CLLCoordType = CLLocationCoordinate2D(latitude: coord.coordinate.latitude,
longitude: coord.coordinate.longitude);
let anno = MKPointAnnotation();
anno.coordinate = CLLCoordType;
mapView.addAnnotation(anno);
}
}
Finally, you need to use the MKMapViewDelegate delegate to use one of its automatically called methods, when a new annotation is added, so we can queue and render it. This would be unnecessary if you were adding annotations once, but it is good to have them added like this so you can manipulate them later.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation{
return nil;
}else{
let pinIdent = "Pin";
var pinView: MKPinAnnotationView;
if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(pinIdent) as? MKPinAnnotationView {
dequeuedView.annotation = annotation;
pinView = dequeuedView;
}else{
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: pinIdent);
}
return pinView;
}
}
Do not just add this code and expect it to work, make sure you incorporate it correctly, in the right areas of your project. Do comment if you have further questions.
UPDATE:
remember to inherit the MKMapViewDelegate protocol into the controller your using.
Make sure you actually call addAnnotations, in ViewDidLoad, and pass through coords array. Which can be defined in ViewDidLoad.
Make sure the mapView method is not in ViewDidLoad but a member of the controller.
Add Simple annotation with title Swift 5.0
let addAnotation = MKPointAnnotation()
addAnotation.title = "YOUR TITLE"
addAnotation.coordinate = CLLocationCoordinate2D(latitude: [YOUR LATITIUDE], longitude: [YOUR LONGITUDE])
self.mapView.addAnnotation(addAnotation)
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard annotation is MKPointAnnotation else { return nil }
let identifier = "Annotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
} else {
annotationView!.annotation = annotation
}
return annotationView
}

Pin correct Annotation Image parse backend

I have setup a map in which data is loaded from the parse backend. In map I want to display the images that are already saved in Assets folder. Image should be equal to Player Name . There latitude and longitude is already saved in the Parse.
The position of the player is displayed correctly but the image is not being showed correctly , if the last name in the playerName column is Sachin then the image is same for all the player annotation pin.
I want to apply like if playerName == annotation.title the image of rahul should be displayed .
Console Print :
26.6481591686873 , 77.1777485870544 Sachin
27.655116868732 , 77.17778705437 Rahul
28.6479996556687 , 77.1779252453013 Matt
28.6480628887157 , 77.1779590059193 Virat
28.1578689896 , 76.989079 Sachin
var playerName:String!
override func viewDidLoad() {
super.viewDidLoad()
query.findObjectsInBackgroundWithBlock { (posts, error) in
if error == nil {
let myPosts = posts! as [PFObject]
for posts in myPosts {
let latitude12 = posts["playerLat"] as! Double
let longitude12 = posts["playerLong"] as! Double
let playName = posts["playerName"] as! String
self.playerName = posts["playerName"] as! String
print(latitude12,"," , longitude12, playerName)
let annotation = MKPointAnnotation()
//let customAnno = MKAnnotationView()
let locationCoordinate = CLLocationCoordinate2DMake(latitude12, longitude12)
annotation.coordinate = locationCoordinate
//customAnno.enabled = true
let anno = self.mapView.dequeueReusableAnnotationViewWithIdentifier("places")
if anno == nil {
// if annotation.title == "Sachin" {
//}
annotation.title = playerName
self.mapView.addAnnotations([annotation])
} else {
anno?.annotation = annotation
}
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "customAnnotationView"
let annotation1 = MKPointAnnotation()
// custom image annotation
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if (annotationView == nil) {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
else {
annotationView!.annotation = annotation
}
if annotation1.title == "Rahul" {
annotationView!.image = UIImage(named: "Rahul")}
else if annotation1.title == "Sachin"{
annotationView!.image = UIImage(named: "Sachin")
}
return annotationView
}
I have tried all codes but the image is same for all the players image.

How to display time on route as google maps

I have created route with multiple annotations.
I want to display text between annotations which exactly as attached screen shot.
Can any one help please?
Thanks
I tried something which will show the distance between two annotation but not when you tap on the MKPolylineOverlay. One more important thing I am not maintaining any standards.
Here is my controller structure.
import UIKit
import MapKit
class RouteViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
//Rest of the code see below
}
First of all I'll add some annotation to map in the viewDidLoad delegate method as below.
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
let annotation1 = MKPointAnnotation()
annotation1.title = "Times Square"
annotation1.coordinate = CLLocationCoordinate2D(latitude: 40.759011, longitude: -73.984472)
let annotation2 = MKPointAnnotation()
annotation2.title = "Empire State Building"
annotation2.coordinate = CLLocationCoordinate2D(latitude: 40.748441, longitude: -73.985564)
let annotation3 = MKPointAnnotation()
annotation3.title = "Some Point"
annotation3.coordinate = CLLocationCoordinate2D(latitude: 40.7484, longitude: -73.97)
let arrayOfPoints = [ annotation1, annotation2, annotation3]
self.mapView.addAnnotations(arrayOfPoints)
self.mapView.centerCoordinate = annotation2.coordinate
for (index, annotation) in arrayOfPoints.enumerate() {
if index < (arrayOfPoints.count-1) {
//I am taking the two consecutive annotation and performing the routing operation.
self.directionHandlerMethod(annotation.coordinate, ePoint: arrayOfPoints[index+1].coordinate)
}
}
}
In the directionHandlerMethod, I am performing the actual request for direction as below,
func directionHandlerMethod(sPoint: CLLocationCoordinate2D, ePoint: CLLocationCoordinate2D) {
let sourcePlacemark = MKPlacemark(coordinate: sPoint, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: ePoint, addressDictionary: nil)
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .Automobile
let directions = MKDirections(request: directionRequest)
directions.calculateDirectionsWithCompletionHandler {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
//I am assuming that it will contain one and only one result so I am taking that one passing to addRoute method
self.addRoute(response.routes[0])
}
}
Next I am adding the polyline route on map in the addRoute method as below,
func addRoute(route: MKRoute) {
let polyline = route.polyline
//Here I am taking the centre point on the polyline and placing an annotation by giving the title as 'Route' and the distance in the subtitle
let annoatation = MKPointAnnotation()
annoatation.coordinate = MKCoordinateForMapPoint(polyline.points()[polyline.pointCount/2])
annoatation.title = "Route"
let timeInMinute = route.expectedTravelTime / 60
let distanceString = String.localizedStringWithFormat("%.2f %#", timeInMinute, timeInMinute>1 ? "minutes" : "minute")
annoatation.subtitle = distanceString
self.mapView.addAnnotation(annoatation)
self.mapView.addOverlay(polyline)
}
Next I am implementing the rendererForOverlay delegate method as below,
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blueColor()
renderer.lineWidth = 2
renderer.lineCap = .Butt
renderer.lineJoin = .Round
return renderer
}
Next one is the important one delegate method which is viewForAnnotation. Here I am doing some things like placing the label instead of the annotation as below,
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.title != nil && annotation.title!! == "Route" {
let label = UILabel()
label.adjustsFontSizeToFitWidth = true
label.backgroundColor = UIColor.whiteColor()
label.minimumScaleFactor = 0.5
label.frame = CGRect(x: 0, y: 0, width: 100, height: 30)
label.text = annotation.subtitle ?? ""
let view = MKAnnotationView()
view.addSubview(label)
return view
}
return nil
}

Swift - setting different images from array to annotation pins

I wonder how to set different images to annotation pins on mapview. The difference between the following questions
viewForAnnotation confusion and customizing the pinColor iteratively
Swift different images for Annotation
is that my array of images is generated dynamically with regard to server response. There is no fixed size of the array, so switch/case construction is not a good idea. Moreover, I'm not sure how to apply the solution with custom class aforementioned in topic above. I'm aware that it would be better to post a comment to one of the questions asked before, but unfortunately I'm too rookie at the moment to do that(too few points).
This is the for loop performed inside functions that shows map:
for var r=0;r<arrayOfRestaurants.count;r++
{
var summonRestaurant:NSDictionary = arrayOfRestaurants[r] as NSDictionary
var nearbyRestaurant = Restaurant(nearbyRestaurants:summonRestaurant)
var latRestaurant=(nearbyRestaurant.latitude as NSString).doubleValue
var longRestaurant=(nearbyRestaurant.longitude as NSString).doubleValue
var locationOfRestaurant = CLLocationCoordinate2D(
latitude: latRestaurant as CLLocationDegrees, longitude: longRestaurant as CLLocationDegrees)
var lunchArray: NSArray = nearbyRestaurant.lunch as NSArray
var annotation = MKPointAnnotation()
annotation.setCoordinate(locationOfRestaurant)
annotation.title = nearbyRestaurant.name + " " + nearbyRestaurant.distance + " km"
map.addAnnotation(annotation)
}
And here is viewForAnnotation delegate method(quite identical to the method used in aforementioned threads):
func mapView(map: MKMapView!,
viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = map.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
pinView!.image = globalImageArray[0]
}
else {
pinView!.annotation = annotation
}
return pinView
}
As you can see, I assigned a certain image to pinView which is globalImageArray[0], but I look for a solution that let me iterate over the globalImageArray and assign a certain image to each pin.
I'd be glad to receive any help, thanks in advance!
First, you need to create your own class that adopts the MKAnnotation protocol for your annotations -
class RestaurantAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String
var subtitle: String
var image: UIImage?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
Then, use instances of this class when you add the annotation and set the image -
for var r=0;r<arrayOfRestaurants.count;r++
{
var summonRestaurant:NSDictionary = arrayOfRestaurants[r] as NSDictionary
var nearbyRestaurant = Restaurant(nearbyRestaurants:summonRestaurant)
var latRestaurant=(nearbyRestaurant.latitude as NSString).doubleValue
var longRestaurant=(nearbyRestaurant.longitude as NSString).doubleValue
let locationOfRestaurant = CLLocationCoordinate2D(
latitude: latRestaurant as CLLocationDegrees, longitude: longRestaurant as CLLocationDegrees)
var lunchArray: NSArray = nearbyRestaurant.lunch as NSArray
let title = nearbyRestaurant.name + " " + nearbyRestaurant.distance +" km"
var annotation = RestaurantAnnotation(coordinate, title:title, subtitle:"")
annotation.image = globalImageArray[r]
map.addAnnotation(annotation)
}
Now, in your view for annotation you can access the image -
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is RestaurantAnnotation) {
return nil
}
let reuseId = "restaurant"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
let restaurantAnnotation = annotation as RestaurantAnnotation
if (restaurantAnnotation.image != nil) {
anView.image = restaurantAnnotation.image!
anView.image.layer.setCornerRadius(8.0)
anView.image.layer.clipsToBounds=true
}
else {
// Perhaps set some default image
}
return anView
}

Resources