zgreycoat
zgreycoat

Reputation: 71

MapKit changes annotation view order when map is scrolled in XCode 4.6

I am an experienced iOS developer, but this has really stumped me. I am simultaneously submitting a problem report to Apple.

I'm adding annotations to a MKMapKit map (Xcode 4.6). Each annotation is a MyAnnotation class; MyAnnotation defines a property, location_id, that I use to keep track of that annotation.

The problem is simple: I want the MyAnnotation with a location_id of -1 to appear in front of everything else.

To do this, I am overriding mapView:didAddAnnotationViews: in my MKMapViewDelegate:

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


// Loop through any newly added views, arranging them (z-index)
for (MKAnnotationView* view in views) {

    // Check the location ID
    if([view.annotation isKindOfClass:[MyAnnotation class]] && [((MyAnnotation*)(view.annotation)).location_id intValue]==-1 ) {

        // -1: Bring to front
        [[view superview] bringSubviewToFront:view];
        NSLog(@"to FRONT: %@",((MyAnnotation*)view.annotation).location_id);

    } else {

        // Something else: send to back
        [[view superview] sendSubviewToBack:view];
        NSLog(@"to BACK: %@",((MyAnnotation*)view.annotation).location_id);
    }
}
}

this works just fine. I have an "add" button that adds an annotation to a random location near the center of my map. Each time I push the "add" button, a new annotation appears; but nothing hides the annotation with the location_id of -1.

** UNTIL ** I scroll!

As soon as I start scrolling, all of my annotations are rearranged (z-order) and my careful stacking no longer applies.

The really confusing thing is, I've done icon order stacking before with no problem whatsoever. I've created a brand new, single view app to test this problem out; sending MKAnnotationView items to the back or front only works until you scroll. There is nothing else in this skeleton app except the code described above. I'm wondering if there is some kind of bug in the latest MapKit framework.

The original problem was in trying to add new annotations when the user scrolled (mapView:regionDidChangeAnimated:). The annotation adds; the mapView:didAddAnnotationViews: code fires; and then the order is scrambled by an unseen hand in the framework (presumably as the scroll completes).

In case you're interested, here is my viewForAnnotation:

-(MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {


// Is this an A91Location?
if([annotation isKindOfClass:[MyAnnotation class]]){

    MyAnnotation* ann=(MyAnnotation*)annotation;

    NSLog(@"viewForAnnotation with A91Location ID %@",ann.location_id);


    if([ann.location_id intValue]==-1){

        // If the ID is -1, use a green pin
        MKPinAnnotationView* green_pin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
        green_pin.pinColor=MKPinAnnotationColorGreen;
        green_pin.enabled=NO;
        return green_pin;

    } else {

        // Otherwise, use a default (red) pin
        MKPinAnnotationView* red_pin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
        red_pin.enabled=NO;
        return red_pin;
    }
}


// Everything else
return nil;


}

And my class:

@interface MyAnnotation : NSObject <MKAnnotation>

@property (strong, nonatomic, readonly) NSNumber* location_id;
@property (strong, nonatomic, readonly) NSString*  name;
@property (strong, nonatomic, readonly) NSString*  description;
@property (nonatomic) CLLocationCoordinate2D coordinate;

-(id) initWithID:(NSNumber*)location_id name: (NSString*) name description:(NSString*) description location:(CLLocationCoordinate2D) location;

// For MKAnnotation protocol... return name and description, respectively
-(NSString*)title;
-(NSString*)subtitle;

@end

Upvotes: 5

Views: 1359

Answers (1)

Tim Vks
Tim Vks

Reputation: 51

Could you possibly try the following as a work around:

1) Remove your code in mapView:didAddAnnotationViews:

2) Pickup when the map is moved or pinched (I assume this is what you consider to be a scroll right?) - I do this with gestures rather than the mapView regionChanged as I have had bad experiences such as unexplained behaviour with the latter.

//recognise the paning gesture to fire the didDragMap method
UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self     action:@selector(didDragMap:)];
[panRec setDelegate:self];
[self.mapView addGestureRecognizer:panRec];

//recognise the pinching gesture to fire the didPinchMap method
UIPinchGestureRecognizer *pinchRec = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(didPinchMap:)];
[pinchRec setDelegate:self];
//[pinchRec setDelaysTouchesBegan:YES];
[self.mapView addGestureRecognizer:pinchRec];

//recognise the doubleTap
UITapGestureRecognizer *doubleTapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPinchMap:)];
[doubleTapRec setDelegate:self];
doubleTapRec.numberOfTapsRequired = 2;
[self.mapView addGestureRecognizer:doubleTapRec];

3) write a custom "plotAnnotations" function to show your annotations. This function will loop through all your annotations and save them in an array "annotationsToShow" ordered by your location id DESC so that your location_id -1 are added last. Use [self.mapView addAnnotations:annotationsToShow]; to display them.

4) Call plotAnnotations in your gestureRecognizer functions

- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
        [self plotAnnotations];
    }
}

- (void)didPinchMap:(UIGestureRecognizer*)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
        [self plotAnnotations];
    }
}

5) You may need to delete all annotations before displaying the new ones in 3) [self.mapView removeAnnotations:annotationsToRemove];

Upvotes: 0

Related Questions