How to define the order of overlapping MKAnnotationViews?
Under iOS 11 the implementation of displayPriority
broke all the solutions which use bringSubviewToFront
or zPosition
.
If you override the annotation view's CALayer, you can wrestle control of zPosition back from the OS.
class AnnotationView: MKAnnotationView {
/// Override the layer factory for this class to return a custom CALayer class
override class var layerClass: AnyClass {
return ZPositionableLayer.self
}
/// convenience accessor for setting zPosition
var stickyZPosition: CGFloat {
get {
return (self.layer as! ZPositionableLayer).stickyZPosition
}
set {
(self.layer as! ZPositionableLayer).stickyZPosition = newValue
}
}
/// force the pin to the front of the z-ordering in the map view
func bringViewToFront() {
superview?.bringSubviewToFront(toFront: self)
stickyZPosition = CGFloat(1)
}
/// force the pin to the back of the z-ordering in the map view
func setViewToDefaultZOrder() {
stickyZPosition = CGFloat(0)
}
}
/// iOS 11 automagically manages the CALayer zPosition, which breaks manual z-ordering.
/// This subclass just throws away any values which the OS sets for zPosition, and provides
/// a specialized accessor for setting the zPosition
private class ZPositionableLayer: CALayer {
/// no-op accessor for setting the zPosition
override var zPosition: CGFloat {
get {
return super.zPosition
}
set {
// do nothing
}
}
/// specialized accessor for setting the zPosition
var stickyZPosition: CGFloat {
get {
return super.zPosition
}
set {
super.zPosition = newValue
}
}
}
Ok, so for solution use method from MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
In this method you should rearrange AnnotationView after it was added to mapKit View. So, code may looks like this:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
for (MKAnnotationView * annView in views) {
TopBottomAnnotation * ann = (TopBottomAnnotation *) [annView annotation];
if ([ann top]) {
[[annView superview] bringSubviewToFront:annView];
} else {
[[annView superview] sendSubviewToBack:annView];
}
}
}
This works for me.