How to change image to an MKAnnotation with Swift - ios

I have a map with 2 annotations and I want 2 different images to each of them.
I know how to do it with one annotation, but my problem is with two annotations.
Here is my code for one:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseId = "pin"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView!.image = nil;
anView!.image = UIImage(named:"destinationPin")
return anView
}
And If anyone can I'd really appreciate if you explain me what is reuseId.
Thanks in advance

First, when adding the annotations to the map, you'll need to differentiate between them. The easy way is to set the tag value. Then you can branch you logic as follows:
if annotation.tag == 0 {
anView!.image = UIImage(named:"destinationPin")
} else {
anView!.image = UIImage(named:"alternateDestinationPin")
}
Note MKAnnotation has other properties like title and coordinate that can be used to branch your logic.

You can cast them as Optionals.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseId = "pin"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if let annotation1 = anView as? FirstAnnotation {
annotation1.image = UIImage(named: "first.jpg")
} else if let annotation2 = anView as? SecondAnnotation {
annotation2.image = UIImage(named: "second.jpg")
}
return anView
}
This has the added benefit of type-safety and not relying on your own knowledge of what's lying inside a tag. If you have two separate MKAnnotation types, you should really sub-class them as two individual classes.

Related

How to show all map pins in mapView even when zoomed out in Swift iOS [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have many pins added to my mapView, but when the map is zoomed out or even closely zoomed in, not all the pins show, unless you zoom in deeply.
How do I make it such that all the pins are shown in a specified zoomed-out level?
side note: I specified the color of the pins to be orange, but after a while, they turn back to red why is that?
here is my code:
func shopsLocations(){
db.collection("Shops").getDocuments() { (querySnapshot, err) in
if err != nil {
} else {
for document in querySnapshot!.documents {
let lat = document["latitude"] as? String
let long = document["longitude"] as? String
self.detailsKey = document["shopPID"] as? String
let myFloat = (lat! as NSString).doubleValue
let myFloat2 = (long! as NSString).doubleValue
let annotation = MyAnnotation(shopPID: document["shopPID"] as! String,coordinate:CLLocationCoordinate2D(latitude: myFloat, longitude: myFloat2),title:(document["name"] as? String)!,subtitle: "Click to view shop details" )
self.mapView.addAnnotation(annotation)
}
}
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKMarkerAnnotationView
if pinView == nil {
pinView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.canShowCallout = true
pinView?.isDraggable = false
pinView?.markerTintColor = UIColor.orange
pinView?.animatesWhenAdded = true
let rightButton: AnyObject! = UIButton(type: UIButton.ButtonType.detailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
pinView?.annotation = annotation
}
return pinView
}

swift - Mapkit changing pinTintColor in if else statement

I'm trying to change pin colour based on a variable - the colour of certain pins change but it seems random and doesn't relate to the colour that I'm expecting. I suspect it's something to do with dequeueReusableAnnotationView.I have tried without but I got the same results. Any ideas ? Thanks
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 {
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pin.canShowCallout = true
annotationView = pin
if globalDataSet[counter].fields.numdocksavailable == 0 {
print (counter,globalDataSet[counter].fields.numdocksavailable, globalDataSet[counter].fields.name)
print ("pin.pinTintColor = UIColor.systemBlue")
pin.pinTintColor = UIColor.systemBlue
} else {
print (counter,globalDataSet[counter].fields.numdocksavailable, globalDataSet[counter].fields.name)
print ("pin.pinTintColor = UIColor.systemRed")
pin.pinTintColor = UIColor.systemRed
}
counter += 1
} else {
annotationView!.annotation = annotation
}
return annotationView
}
Try the following code.
if annotationView == nil {
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pin.canShowCallout = true
annotationView = pin
} else {
annotationView!.annotation = annotation
}
if globalDataSet[counter].fields.numdocksavailable == 0 {
print (counter,globalDataSet[counter].fields.numdocksavailable, globalDataSet[counter].fields.name)
print ("annotationView.pinTintColor = UIColor.systemBlue")
annotationView.pinTintColor = UIColor.systemBlue
} else {
print (counter,globalDataSet[counter].fields.numdocksavailable, globalDataSet[counter].fields.name)
print ("annotationView.pinTintColor = UIColor.systemRed")
annotationView.pinTintColor = UIColor.systemRed
}
counter += 1

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
}

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
}

Parse objects as AnnonationPoints

I'm trying to show a set of parse objects in a MKMapView as Annonations. This seem to work, the problem is that the objects images are swapped, which is kind of random? i've started creating a subclass of the MKPointAnnotation in order to save the image. How come the images are swapped around?
Custom MKPointAnnonation class
class AnnonationClass: MKPointAnnotation {
var itemId: NSString?
var itemImage: PFFile?
var itemdescription: NSString?
}
getting the parse objects and adding them using the setGeoPoint method
override func objectsDidLoad(error: NSError!) {
super.objectsDidLoad(error)
if error == nil {
self.pointMapView?.removeAnnotations(pointMapView?.annotations)
for object in objects {
var theObject = object as PFObject
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd" // superset of OP's format
let str = dateFormatter.stringFromDate(object.createdAt)
self.setGeoPoint(theObject.objectForKey("location") as PFGeoPoint, titleString: theObject.objectForKey("title") as NSString, imageFile: theObject.objectForKey("image") as PFFile, theId: theObject.objectId as NSString, descString: theObject.objectForKey("description") as NSString, dateString: str)
}
}
}
Setting the coordinates, title and saving the imageFile in the itemImage
func setGeoPoint(geoPoint:PFGeoPoint, titleString:NSString, imageFile:PFFile, theId:NSString, descString:NSString, dateString:NSString) {
var coordinate = CLLocationCoordinate2DMake(geoPoint.latitude, geoPoint.longitude) as CLLocationCoordinate2D
var pinView:AnnonationClass = AnnonationClass()
pinView.itemImage = imageFile
pinView.setCoordinate(coordinate)
pinView.title = titleString
pinView.subtitle = dateString
self.pointMapView!.addAnnotation(pinView)
}
creating the image and setting it
func mapView(mapView: MKMapView!, viewForAnnotation annotation: AnnonationClass!) -> MKAnnotationView! {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
var leftImage: PFImageView = PFImageView(frame: CGRectMake(0, 0, 44, 44))
leftImage.file = annotation.itemImage
leftImage.loadInBackground()
pinView!.leftCalloutAccessoryView = leftImage
}
else {
pinView!.annotation = annotation
}
return pinView
}
In your viewForAnnotation, when pinView != nil, i.e. when reusing a view, it could well be that the image is set from the previous time that view was used. I think it is analogous to setting check marks on table view cells instead of using the data source to properly set properties for the cell contents.
Try setting the image view whether or not the pinView is reused.
func mapView(mapView: MKMapView!, viewForAnnotation annotation: AnnonationClass!) -> MKAnnotationView! {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Purple
}
else {
pinView!.annotation = annotation
}
var leftImage: PFImageView = PFImageView(frame: CGRectMake(0, 0, 44, 44))
leftImage.file = annotation.itemImage
leftImage.loadInBackground()
pinView!.leftCalloutAccessoryView = leftImage
return pinView
}

Resources