Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I want to display different colour pins in a UIMapView based on the relative time they represent
but it seems the mapView:viewForAnnotation: method only does it's thing independent of when its called.

In my code example I have already retrieved earlier & newer locations from a file to self.fileArray . the array holds objects called findings that has (among others) an age property . newest findings start life as age @"0", and each time the array is reloaded ready to take new findings they progress in age to @"1" and @"2" respectively after which they are then dropped.

Once they take on their new age property they are sent to the mapView:viewForAnnotation: method to be displayed according to their new status as I iterate through the fileArray

the actual question is after the jump. A lot of interesting other-answers cropped up while formulating the question but none quite applied to my case

.
.
int size = [self.fileArray count];
for (int idx=(size-1); idx>0; idx--)  // process backwards
    {
        annotationFlag = 0;           // using a global just for now
    self.finding = self.fileArray[idx];
        if ([self.finding.age isEqualToString:@"2"]) {
            [self.fileArray removeObjectAtIndex:idx];
        }

        if ([self.finding.age isEqualToString:@"1"]) {
            self.finding.age = @"2";
            [self.fileArray replaceObjectAtIndex:idx withObject:self.finding];
            annotationFlag = 2;

            // tried here , only displays the newest
        }

        if ([self.finding.age isEqualToString:@"0"]) {
            self.finding.age = @"1";
            [self.fileArray replaceObjectAtIndex:idx withObject:self.finding];
            annotationFlag = 1;

            // tried here, still only displays the same newest 
        }

    }   // end if

//<Breakpoint with Log here> 

    MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
    CLLocationCoordinate2D myCoordinate;
    myCoordinate.latitude =[self.finding.myLat doubleValue];
    myCoordinate.longitude=[self.finding.myLong doubleValue];
    annotation.coordinate = myCoordinate;
    [self.mapView addAnnotation:annotation];
}       // end for 
.
.

the annotation methods are fairly standard, as used by most everybody:

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation: (MKUserLocation *)userLocation {
_mapView.centerCoordinate = userLocation.location.coordinate;
}


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

if([annotation isKindOfClass:[MKUserLocation class]])
    return nil;

static NSString *identifier = @"myAnnotation";
MKPinAnnotationView * annotationView = (MKPinAnnotationView*)[ self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!annotationView)
{
    annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];

//<Breakpoint with Log here>

    switch (annotationFlag) {
        case 1:
        annotationView.pinColor = MKPinAnnotationColorGreen;
            break;
        case 2:
        annotationView.pinColor = MKPinAnnotationColorRed;
            break;
        default:
        annotationView.pinColor = MKPinAnnotationColorPurple;
        break;
    }
    annotationView.animatesDrop = YES;
    annotationView.canShowCallout = NO;
}else {
    annotationView.annotation = annotation;
}
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeSystem]; // UIButtonTypeDetailDisclosure
return annotationView;
}

also under test is my neighbours dogs curiosity. the pins should show different colours for each foray <screenshot>

If I NSLog annotationFlag to console at various points mapView:viewForAnnotation: seems to be ignoring the values in annotationFlag and only using the state last set, leading me to believe it is only acting when the for loop is entirely finished, and not following iterations.

so the question is, why isn't the [self.mapView addAnnotation:annotation] call acting immediately. Ive put it within the for loop, and there is no doubling up happening there.

LATE EDIT: using a combination of breakpoints and log-to-consoles as shown in the listings above, and commenting out the age increase processing results in an array of 42 elements ( including the old ones ready to be discarded ) and therefore 42 pins to be dropped.

When the mapView:viewForAnnotation method is reached I then have to step through for another 42 times and on the 43rd all the pins drop at once. Watching carefully its the same colour so I can verify the last colour used doesn't draw over any earlier ones. If that clarifies the problem.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
463 views
Welcome To Ask or Share your Answers For Others

1 Answer

There is no guarantee that the viewForAnnotation will be called immediately after addAnnotation or that it will be called only once.

The annotation could be added in a region that isn't currently visible or the user might pan or zoom the map which causes the annotation to come back into view. The map view will simply call it whenever or as often as it needs to.

This is by-design and simply how the delegate method approach works.

For this reason, your implementation of the delegate method should generally only use the annotation parameter passed to the method as the basis for all the logic inside the method. It should not rely on any external variables or make broad assumptions about when it will be called.

For other answers that may explain this in more detail, see:

For your question specifically, I suggest the following:

  1. Right now you're adding annotations of type MKPointAnnotation which don't contain the "age" information that the viewForAnnotation method needs (I'm assuming this is what it needs).

    Instead of using MKPointAnnotation, make your Finding class (or whatever the type is of the self.finding object) implement the MKAnnotation protocol itself. You should be able to find several examples of custom annotation classes on SO.

    Then, instead of keeping an annotationFlag variable and creating MKPointAnnotation objects, add the Finding objects themselves (which contain their "age") directly to the map when calling addAnnotation.

  2. In viewForAnnotation, set the pinColor after the if-else that creates/dequeues the view and just before the return. Be sure to set the pinColor based on the age property of the annotation object passed into the method (which will be a Finding type object). For example:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation 
    {
        if([annotation isKindOfClass:[MKUserLocation class]])
            return nil;
    
        static NSString *identifier = @"myAnnotation";
        MKPinAnnotationView * annotationView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (!annotationView)
        {
            annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            annotationView.animatesDrop = YES;
            annotationView.canShowCallout = NO;
            annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeSystem];
        }else {
            annotationView.annotation = annotation;
        }
    
        //update the pinColor in the view whether it's a new OR dequeued view...
        if ([annotation isKindOfClass:[Finding class]])
        {
            Finding *f = (Finding *)annotation;
    
            if ([f.age isEqualToString:@"2"]) {
                annotationView.pinColor = MKPinAnnotationColorGreen;
            }
            else if ([f.age isEqualToString:@"1"]) {
                annotationView.pinColor = MKPinAnnotationColorPurple;
            }
            else {
                annotationView.pinColor = MKPinAnnotationColorRed;
            }
        }
    
        return annotationView;
    }
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...