Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define the order of overlapping MKAnnotationViews?

Tags:

iphone

mapkit

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.


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
        }
    }
}

Try to setup annotation view layer's zPosition (annotationView.layer.zPosition) in:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views;

Swift 3:

I get pin locations from API and I was having similar issues, the pins that had to be on top weren't. I was able to solve it like this.

var count = 0 // just so we don't get the same index in bottom pins
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {  
    for view in views {
        view.layer.zPosition = CGFloat(count)
    }
    count += 1
    if count > 500 {
        count = 250 // just so we don't end up with 999999999999+ as a value for count, plus I have at least 30 pins that show at the same time and need to have lower Z-Index values than the top pins. 
    }

}

Hope this helps


In the delegate function, you can select the pin to force it on top:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?` {
    ...
    if my annotation is the special one {
        annotationView.isSelected = true
    }
    ...
}