MKAnnotationViews and AutoLayout

Having just developed a plist-based design framework that works in Interface Builder, I’m more interested than ever in using xibs and storyboards to design my views.

I also like using AutoLayout. There are significant advantages when it comes to things like labels that make it so much easier to deal with than manually setting frames everywhere in code. Things like Dynamic Type, accessibility, and localisation all become easier and there’s less room for error.

There are some things that do become more complicated with AutoLayout (mostly transforms) but there are well established workarounds for most of these.

I was recently designing a new app that involved using MapKit and I wanted to use AutoLayout to design a subclsss of MKAnnotationView but this isn’t entirely straightforward.

When a view is first dequeued, its frame is set directly by the map. This means that any MKAnnotationView subclass cannot switch off translatesAutoresizingMaskIntoConstraints, which is required to get AutoLayout working correctly.

The subview can’t have AutoLayout resetting its frame when it’s finished it’s calculation passes. If this property is set to false, AutoLayout will move the view up to the top left corner of the map view (as it sets the subclass frame without any further position information, i.e. constraints to position the annotation view in the map view itself) . Manually dragging the map triggers a reset of the annotation view’s position, but this is still an unacceptably awful UI experience.

What the MKAnnotationView subclass needs is an additional container view: one who can catch the setting of the frame by AutoLayout and discard the position information.

The Xib file already has a UIView attached, which is the contentView. We then need to add an additional containerView, which is pinned to the contentView. I am then free to use AutoLayout to layout all of my other internal views. This containerView is what allows the whole thing to be resized to fit the contents.

Image of the view hierarchy for the Xib file

During the setup phase, we load the Nib and attach the contentView. We then directly set the MKAnnotationView‘s bounds (not frame, so no position information is affected). Then we centre the containerView to the MKAnnotationView's center (i.e. its grandparent view, not its direct parent) so that it gets its position information from the AnnotationView.

Finally, when layoutSubviews or prepareForReuse are called on the annotation view, the MKAnnotationView gets its size information from the containerView and adjusts accordingly, again ignoring the position information (which comes from the MKMapView it belongs to).

AutoLayout passes can be expensive if there are a lot of them which, on a map view with many annotations, there might be. Performance testing on real devices will be crucial.

Still, on modern devices this shouldn’t be so much of an issue and AutoLayout makes it easier to design views and uses a lot less code than manually setting frames. There are also advantages to be had in terms of getting a lot of work done for free by AutoLayout when dynamically adding new views or animating existing ones (e.g. using Stack Views to hide or show content).

The example project is available on GitHub.

If you you enjoyed this, sign up to my mailing list to get my latest posts direct to your inbox.

Your email won't be shared and you can unsubscribe any time (the link’s at the bottom of every email).