I have a working loop to setup annotations for the title and subtitle elements for some working data points. What I want to do within that same loop structure is to set the pin color to Purple instead of the default. What I can't figure out is what I need to do to tap into my theMapView to set the pin accordingly.
My working loop and some attempts at something...
....
for var index = 0; index < MySupplierData.count; ++index {
// Establish an Annotation
myAnnotation = MKPointAnnotation();
... establish the coordinate,title, subtitle properties - this all works
self.theMapView.addAnnotation(myAnnotation) // this works great.
// In thinking about PinView and how to set it up I have this...
myPinView = MKPinAnnotationView();
myPinView.animatesDrop = true;
myPinView.pinColor = MKPinAnnotationColor.Purple;
// Now how do I get this view to be used for this particular Annotation in theMapView that I am iterating through??? Somehow I need to marry them or know how to replace these attributes directly without the above code for each data point added to the view
// It would be nice to have some kind of addPinView.
}
You need to implement the viewForAnnotation delegate method and return an MKAnnotationView (or subclass) from there.
This is just like in Objective-C -- the underlying SDK works the same way.
Remove the creation of MKPinAnnotationView from the for loop that adds the annotations and implement the delegate method instead.
Here is a sample implementation of the viewForAnnotation delegate method in Swift:
func mapView(mapView: 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 = 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
}
return pinView
}
Related
I want to show a UIView just above the pin location and if the user moves around the map the UIView should remain above the pin location. I dont want to use the callout bubble. Is there any other way?
in iOS 9 we have a new property named detailCalloutAccessoryView
You can create a view and set as
annotationView.detailCalloutAccessoryView = tempView
Please check the link to get more details
MapKit iOS 9 detailCalloutAccessoryView usage
Try using this code:
func mapView(_ mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView {
if (annotation is MKUserLocation) {
return nil
}
else if (annotation is YourAnnotationClassHere) {
let identifier = "MyCustomAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView {
annotationView.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation, reuseIdentifier: identifier)
}
annotationView.canShowCallout = false
// set to YES if using customized rendition of standard callout; set to NO if creating your own callout from scratch
annotationView.image = UIImage(named: "your-image-here.png")!
return annotationView
}
return nil
}
That's mainly what you need for that to work. This is a swift version of this answer right here: How to create Custom MKAnnotationView and custom annotation title and subtitle
Hope it helped!!!
I have followed other stack posts and tutorials in changing my annotations image to a custom one, but it does not seem to work. No errors or runtime errors appear, it's just that the annotation image does not change.
Just by the way I set a break point on the line annotationView!.image = UIImage(named: "RaceCarMan2png.png") and it shows that the line is being called, but yet nothing happens. I would really appreciate your help. Thanks.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "MyCustomAnnotation"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
annotationView!.image = UIImage(named: "RaceCarMan2png.png")
} else {
annotationView!.annotation = annotation
}
configureDetailView(annotationView!)
return annotationView
}
func configureDetailView(annotationView: MKAnnotationView) {
annotationView.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "url.jpg"))
}
The issue is that you're using MKPinAnnotationView. If you use MKAnnotationView, you will see your image.
In the past (e.g. iOS 8), setting the image of MKPinAnnotationView seemed to work fine, but in iOS 9 and later, it uses a pin, regardless (which is not entirely unreasonable behavior for a class called MKPinAnnotationView; lol).
Using MKAnnotationView avoids this problem.
Call that before return
if let annotationView = annotationView {
// Configure your annotation view here
annotationView.image = UIImage(named: "RaceCarMan2png.png")
}
return annotationView
and check with breakpoint if it is ok inside if
I am trying to customize the annotations on the map, and instead of pins I want the annotations to look like the current location indicator. How would I do this? Any advice would be great!
In general you can use custom annotation object which extends MKPointAnnotation. But if you just need to change pin image you can avoid subclassing and just implement this method
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if let annotationView = annotationView {
annotationView.annotation = annotation
} else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView.image = UIImage(named: "myPinImage")
}
return annotationView
}
So it's enough for you to find correct image and replace pins with it.
But if you want to follow possible changes of default currentLocation image you can reuse default view
let annotationView = mapView.viewForAnnotation(mapView.userLocation());
Is there a way to trigger a tapped event (on a map pin) without the use of callouts?
I tried to implement the didSelectAnnotationView below, but it did not seem to work:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if let annotation = annotation {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView = self.mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = false
view.enabled = true
}
return view
}
return nil
}
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
println("test")
}
The didSelectAnnotationView is the correct method. If you're not seeing it called, it's likely that the map view's delegate has not been set.
The default viewForAnnotation behavior will render something very close to what yours does, so it might not be immediately obvious that the delegate wasn't set properly. You might want to put a log/breakpoint in viewForAnnotation (or do something that makes it more visually distinct, e.g. different pin color) and confirm whether the delegate methods are getting called at all.
I'm trying to drop some pins that represent bus stops with an image, when I ad the image it changes the placement of the pin. When I do not set an image the pin is dropped in the correct place.
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is StopAnnotation {
let identifier = "stopAnnotation"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if pinView == nil {
//println("Pinview was nil")
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pinView!.canShowCallout = true
pinView.image = UIImage(named: "stopIcon")
}
return pinView
}
return nil
}
Examples
The image I am trying to use:
Can anyone tell me why this is doing this? I am using the exact same image in my Obj-C version of this app and everything is working fine.
The code is creating an MKPinAnnotationView with a custom image.
The MKPinAnnotationView class should only be used to display the default pin images.
To show a custom image, it's better to use a plain MKAnnotationView.
Because the code is using an MKPinAnnotationView, the image is automatically getting an offset applied to it (the centerOffset property).
This built-in offset works for the default pin images but not for your custom image.
Rather than trying to override this default behavior, use a plain MKAnnotationView instead:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is StopAnnotation {
let identifier = "stopAnnotation"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if pinView == nil {
//println("Pinview was nil")
//Create a plain MKAnnotationView if using a custom image...
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pinView!.canShowCallout = true
pinView.image = UIImage(named: "stopIcon")
}
else {
//Unrelated to the image problem but...
//Update the annotation reference if re-using a view...
pinView.annotation = annotation
}
return pinView
}
return nil
}