Friday, February 1, 2013

Fun With Annotations

I have recently experienced the joy of creating custom map annotations for an iOS project. I found many examples online of how to create custom annotations .. none of them were simple. That surprised me. The problem is simple .. the solution should be equally simple.

First, let's all blame Apple for really missing the boat on this one. They could have made this very easy for us by supplying a convenient way to hook into the standard callout view for an annotation. Most of us simply want to add some additional views to the callout. To achieve this Apple could have supplied an addSubviewToCallout: method. Or better yet, Apple could have provided a mapView:calloutViewForAnnotation: method. This follows their own convention and would have made our lives easier.

The solutions I saw online are problematic .. some do not support panning and zooming. They are kludgey .. some require you to add a new annotation to the map as a phony callout. All seem goofy to me. So, out of frustration I developed my own method.

My solution is also a kludge (Apple is the only one that can fix this) .. but a fairly elegant one. Here is what I did.


Create a Custom Annotation

Create a subclass of MKAnnotationView, let's call it MyAnnotationView. It is important subclasses of MKAnnotationView are initialized using the initWithAnnotation:reuseId method. My initializer also takes a third parameter .. the name of a nib from which it loads the callout view. This allows for easy reuse of the solution.

initWithAnnotation:reuseId:nibName

Expose the Callout View

I added a calloutView property to MyAnnotationView so the map delegate could retrieve it later. MyAnnotationView doesn't know or care what you put into the callout view. It's up to the map delegate to perform any setup for the callout view.

Show the Callout on Selection

I implemented mapView:didSelectAnnotationView: and mapView:didDeselectAnnotationView: methods for the map delegate. These call corresponding methods on MyAnnotationView. When the annotation is selected, it's callout view is added as a subview to the annotation view. I place the callout above the annotation view, just like the standard callout. When the annotation is deselected, I call it's removeFromSuperview method.

What's Missing?

As you an see my annotation doesn't have the fancy pointer at the bottom like the standard callout. That's easy to remedy. Again, I'm loading the callout from a nib, so I could simply decorate the view in IB with a border image that points down. I will probably spend lots of time making this look just so, but my main objective -- create a reusable custom annotation -- has been accomplished.


No comments: