Issue with Map Annotation and MKMapView in iOS 4.2

2020-02-14 08:09发布

问题:

I have a map view with pins that when the user selects a pin it goes to a detail screen for that pin. I also have a table view that when the user selects an item it goes to the same type detail view.

Here's the problem ...

It seems to work fine in everything from 3.1.3 to 4.1. That is the detail view matches with the pin. But I have a user who just upgraded to iOS 4.2 and says that under 4.1 it worked fine, but in 4.2 the pin selection takes him to the detail for a different pin. But in the table view it still works fine.

Is it possible that there was a change in MKMapView in iOS 4.2 that changes how the pinID is selected?

Addition - I have added the relevant parts of viewForAnnotation and checkButtonTapped

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

int postTag = 0;

MKPinAnnotationView *pinView = (MKPinAnnotationView *)[eMapView dequeueReusableAnnotationViewWithIdentifier:@"Pin"];

if(pinView == nil) {

    pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Pin"];
    pinView.frame = CGRectMake(0, 0, 25, 25);

} else {

    pinView.annotation = annotation;

}   

// Set up the Right callout
UIButton *myDetailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
myDetailButton.frame = CGRectMake(0, 0, 23, 23);
myDetailButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myDetailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;

[myDetailButton addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

// Identify which pin is being selected
if ([[annotation title] isEqualToString:@"Current Location"]) {
    postTag = 99999;
} else {
    postTag = [annotation getPinID];

} 

myDetailButton.tag  = postTag;

pinView.rightCalloutAccessoryView = myDetailButton;

pinView.animatesDrop = YES;

// Set to show a callout on the pin
pinView.canShowCallout = YES;

return pinView;
}

// Method to show detail view when the callOut button is selected
- (IBAction) checkButtonTapped: (id) sender {

int nrButtonPressed = ((UIButton *)sender).tag;

if (nrButtonPressed < 99999) {
    if (self.showDetailView == nil) {

        DetailData *tmpViewController = [[DetailData alloc] initWithNibName:@"Detail" bundle:nil];
        self.showDetailView = tmpViewController;
        [tmpViewController release];

    }

    buttonDetail = [[mapView annotations] objectAtIndex:(nrButtonPressed-1)];

    NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects: [buttonDetail getDataI], [buttonDetail getDataII], [buttonDetail getDataIII], [buttonDetail getDataIV], [buttonDetail getDataV], nil];

    self.showDetailView.eventData = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES];

    [self.navigationController pushViewController:self.showDetailView animated:YES];

    [self.showDetailView.eventData release];

}

}

As you can see, in checkButtonTapped I record the pin selected then collect the data from the annotation for that pin. The problem seems to be that nrButtonPressed is now incorrect. But it is fine when compiled in 4.1

回答1:

In checkButtonTapped, the annotation is being identified using the button's tag. The button's tag is set in viewForAnnotation by calling the custom method getPinID.

The button tag is used as the index into the mapView annotations array. It's not clear how the getPinID method figures out what index it is at in the mapView's annotations array. I'm not sure it's wise to assume an annotation is going to be at a specific index in the array. Even if mapView doesn't shuffle the annotations around, your app might be constantly adding and removing annotations.

Because you are assigning the button as a callout accessory in the annotation view, rather than using a button tag to identify the annotation, you can use the mapView's own mapView:annotationView:calloutAccessoryControlTapped: delegate method.

Instead of doing addTarget on the button and having it call your custom method, remove the call to addTarget and implement calloutAccessoryControlTapped.

In calloutAccessoryControlTapped, you can directly access the annotation using view.annotation. You can also easily check if the annotation is the "user location":

 - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
    {
        if (view.annotation == mapView.userLocation)
            return;

        buttonDetail = (MyCustomAnnotationClass *)view.annotation;

        //show detail view using buttonDetail...
    }

Additionally, in viewForAnnotation, you are creating a button every time even if the view is being re-used. So a re-used annotation view probably ends up getting multiple buttons on top of each other. The button should only be created and set in the if(pinView == nil) section.



回答2:

If anyone would care to see what I am using now (and it is working) ...

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

    MKPinAnnotationView *pinView = (MKPinAnnotationView *)[eMapView dequeueReusableAnnotationViewWithIdentifier:@"Pin"];

    if(pinView == nil) {

        pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Pin"];
        pinView.frame = CGRectMake(0, 0, 25, 25);

    } else {

        pinView.annotation = annotation;

    }   

    // Set up the Right callout
    UIButton *myDetailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    myDetailButton.frame = CGRectMake(0, 0, 23, 23);
    myDetailButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    myDetailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;

    pinView.rightCalloutAccessoryView = myDetailButton;

    pinView.animatesDrop = YES;

    // Set to show a callout on the pin
    pinView.canShowCallout = YES;

    return pinView;
}

// Added this at the suggestion of aBitObvious on StackOverflow - 120810
- (void)mapView:(MKMapView *)eMapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {

    if (view.annotation == eMapView.userLocation)
        return;

    if (self.showDetailView == nil) {

        DetailData *tmpViewController = [[DetailData alloc] initWithNibName:@"DetailData" bundle:nil];
        self.showDetailView = tmpViewController;
        [tmpViewController release];

    }

    buttonDetail = (MyCustomAnnotationClass *)view.annotation;

    NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects: [buttonDetail getDataI], [buttonDetail getDataII], [buttonDetail getDataIII], [buttonDetail getDataIV], [buttonDetail getDataV], nil];

    self.showDetailView.eventData = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES];

    [self.navigationController pushViewController:self.showDetailView animated:YES];

    [self.showDetailView.eventData release];

}

Thanks again!