Friday, February 8, 2013

Fixed Gradient Strips

Last time I posted my solution to custom annotations on iOS. In the week since I have had a chance to refine that solution a bit. While it's still not ready for prime time it keeps edging closer. The most recent issue I have tackled is how to make the custom annotation window have a very similar look to that of the standard annotation window. You know the look .. translucent gradient on top with a slightly more opaque solid color on bottom. Sounds easy, right? Well it is easy when all of your annotation windows are the same height. But what would be the point of a custom annotation window with a fixed height?

I could use a standard gradient strip and stretch it to the height of the window, but that would mean the gradient would appear to peak at different heights on different sized windows. Too sloppy. Instead I created a fixed size gradient strip about the same height as the one you see on the standard annotation window .. I chose 26 points. When loading my custom annotation window I build two background views: upperView and lowerView. The upperView is 26 points high and uses the gradient strip as it's tiled background. The lowerView is the remaining height of the view (self.bounds.size.height - 26) and has a solid color the same as the ending color of the gradient. This produces a satisfying and consistent annotation window regardless of the overall height.

This is a simple solution to my problem. Got your own ideas about how to solve this? Leave me a note and tell me what you think.

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.