Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling overlapping MKAnnotationView(s)

How can we ensure that the annotation views are placed in a specified order/ position on the map?

I have a case where a single address can have multiple annotations (on top of each other). For example, Pin numbers 1, 2, 3, and 4 are placed at the same geo-location. When the map loads, I want Pin # 1 to be on the top, but there's no way to ensure that Pin # 1 will always be visible (on the top).

Looking around I found the following solutions, but none of these have worked for me.

1). Add the annotations to the map, in the order you want it.

In my experience, it doesn't work! In my experience, adding annotations together using addAnnotations: or adding them one by one generates the same result. The order of annotation views are still random.

[self.mapView addAnnotations:allAnnotations];

Generates the same result as:

for (MyCustomAnnotation *annotation in allAnnotations) {
    [self.mapView addAnnotation:annotation];
}

2). Use bringSubviewToFront or sendSubviewToBack to specify the order of all annotations.

In my experience it doesn't work! For example, the following code doesn't generate the required results:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
    // .tag associated with each view specifies the order/hierarchy of annotation views
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"tag" ascending:YES];
    NSArray *orderedAnnotationViews = [views sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

    for (MKAnnotationView *annotationView in orderedAnnotationViews) {
        [mapView sendSubviewToBack:annotationView];
    }
}

3). Use annotationView.layer.zPosition to specify the exact order of all annotations.

Consider a scenario where 4 pins are overlapping e.g. Pin 1, 2, 3, and 4. The problem with this approach is that tapping on a specific annotation pin or programmatically selecting it doesn't bring it to the front. The zPosition is absolute. You can change it every time a pin is selected, but that's not ideal.

The zPosition property specifies the z-axis component of the layer's position. The zPosition is intended to be used to set the visual position of the layer relative to its sibling layers. It should not be used to specify the order of layer siblings, instead reorder the layer in the sublayer array.

Thoughts?

like image 633
Mustafa Avatar asked Dec 26 '14 10:12

Mustafa


1 Answers

I ended up working with zPosition:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"tag" ascending:NO];
    NSArray *sortedViews = [views sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

    // Set zPosition
    for (MKAnnotationView *annotationView in sortedViews) {
        annotationView.layer.zPosition = ++_zPositionForAnnotationViews;
    }

    // Do whatever
    // . . .
}

- (void)mapView:(MKMapView *)aMapView didSelectAnnotationView:(MKAnnotationView *)view {
    NSLog(@"The annotation tapped is: %@", view.annotation.title);

    // Ignore tap on user location
    if ([view.annotation isKindOfClass:[MKUserLocation class]]) {
        return;
    }

    // Update zPosition (to bring it to front)
    view.layer.zPosition = ++_zPositionForAnnotationViews;

    // Do whatever
    // . . .
}

Where _zPositionForAnnotationViews is a class variable.

like image 107
Mustafa Avatar answered Oct 30 '22 16:10

Mustafa