Callout opening wrong view after zoom in iOS7

ε祈祈猫儿з 提交于 2020-01-05 10:38:07

问题


Everything is working fine in my app except for one thing: after zooming in and zooming back out, to see the whole map, some callouts open the wrong detailview. I don't know if I'm missing some code or else. Using Xcode 5.1.1 for iOS7. This is what I've got at the moment:

Annotation.h

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface Annotation: NSObject <MKAnnotation>

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

@end

Annotation.m

#import "Annotation.h"

@implementation Annotation
@synthesize coordinate,title,subtitle;

@end

MapView.h

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface Nameofthemap : UIViewController <MKMapViewDelegate>

@property (strong, nonatomic) IBOutlet MKMapView *Nameofthemap;

@end

MapView.m

#import "MapView.h"
#import "Annotation.h"
#import "InfoViewController.h"
#import "InfoTwoViewController.h"

@interface MapView ()
@property (nonatomic, strong) IBOutlet InfoViewController *InfoViewController;

@property (nonatomic, strong) IBOutlet InfoTwoViewController *InfoTwoViewController;

@end

#define PLACE1_LATITUDE 43.777130;
#define PLACE2_LONGITUDE 10.790018;

#define PLACE2_LATITUDE 43.81471237;

#define PLACE2_LONGITUDE 10.67472765;

@implementation MapView

- (IBAction)changeMapType:(id)sender {
    if (_MapView.mapType == MKMapTypeHybrid)
    _MapView.mapType = MKMapTypeStandard;
else
    _MapView.mapType = MKMapTypeHybrid;
}

- (void)viewDidLoad
{
[super viewDidLoad];

[self gotoLocation];

_MapView.showsUserLocation = YES;


}
- (void)gotoLocation
{
MKCoordinateRegion newRegion;

newRegion.center.latitude = PLACE1_LATITUDE;
newRegion.center.longitude = PLACE2_LONGITUDE;

newRegion.span.latitudeDelta = 0.25f;
newRegion.span.longitudeDelta = 0.25f;

[self.MapView setRegion:newRegion animated:YES];

NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation *myAnn;

Annotation *myAnn2;

//Place1 annotation
myAnn = [[Annotation alloc] init];
location.latitude = PLACE1_LATITUDE;
location.longitude = PLACE1_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = @"Name of the place";
myAnn.subtitle = @"Details";
[locations addObject:myAnn];

//Place2 annotation
myAnn2 = [[Annotation alloc] init];
location.latitude = PLACE2_LATITUDE;
location.longitude = PLACE2_LONGITUDE;
myAnn2.coordinate = location;
myAnn2.title = @"Name of place two";
myAnn2.subtitle = @"Details";

[locations addObject:myAnn2];

[self->_MapView addAnnotations:locations];

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view     calloutAccessoryControlTapped:(UIControl *)control{

}

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
    ((MKUserLocation *)myAnn).title = @"Your position";
    return nil;
}
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView  dequeueReusableAnnotationViewWithIdentifier:@"pinView"];
if (!pinView) {
    pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:@"pinView"];
    pinView.pinColor = MKPinAnnotationColorRed;
    pinView.canShowCallout = YES;

    UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    if ([[myAnn title] isEqualToString:@"Name of the place"]){
        [rightButton addTarget:self  action:@selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
    }
    if ([[myAnn title] isEqualToString:@"Name of place two"]){
        [rightButton addTarget:self action:@selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];

    }
pinView.rightCalloutAccessoryView = rightButton;
}
    return pinView;
}

-(IBAction)myAnnClicked:(id)sender
{
InfoViewController *info = [[InfoViewController alloc]init];
[self.navigationController pushViewController:info animated:YES];
}
-(IBAction)myAnn2Clicked:(id)sender
{
InfoTwoController *info2 = [[InfoTwoController alloc]init];
[self.navigationController pushViewController:info2 animated:YES];

}
@end

回答1:


It's an annotation view re-use issue.

In viewForAnnotation, the button targets are only being set when creating a view (if dequeueReusableAnnotationViewWithIdentifier returns nil).

But if dequeueReusableAnnotationViewWithIdentifier returns a previously-used view, the button target is still whatever was set for the annotation that used the view before.

That previous annotation may not be the same as the current annotation.

So it's possible for annotation "two" to re-use a view that was originally created for annotation "one" and tapping on the already-created button shows the info for "one" instead of "two".

To fix this, two things should be done:

  1. If dequeueReusableAnnotationViewWithIdentifier returns a view (if pinView is not nil), the code must update the view's annotation property to the current annotation.
  2. The button target must be set whether a new view is being created or a dequeued view is being re-used. The easiest way to do this is to move the button creation/setting after the main if and just before the return.

The updated viewForAnnotation would look like this:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
    if ([myAnn isKindOfClass:[MKUserLocation class]])
    {
        ((MKUserLocation *)myAnn).title = @"Your position";
        return nil;
    }

    MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView  dequeueReusableAnnotationViewWithIdentifier:@"pinView"];
    if (!pinView) {
        pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:@"pinView"];
        pinView.pinColor = MKPinAnnotationColorRed;
        pinView.canShowCallout = YES;
    }
    else
    {
        //1. Re-using a view, update which annotation it's being used for now
        pinView.annotation = myAnn;
    }

    //2. Now pinView is either a new view or re-used view.
    //Set its button target based on current annotation...

    UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    if ([[myAnn title] isEqualToString:@"Name of the place"]){
        [rightButton addTarget:self  action:@selector(myAnnClicked:)forControlEvents:UIControlEventTouchUpInside];
    }
    if ([[myAnn title] isEqualToString:@"Name of place two"]){
        [rightButton addTarget:self action:@selector(myAnn2Clicked:)forControlEvents:UIControlEventTouchUpInside];

    }
    pinView.rightCalloutAccessoryView = rightButton;

    return pinView;
}



By the way, instead of creating separate methods for each annotation (which can get tedious), use the map view's calloutAccessoryControlTapped delegate method instead.

In fact, right now, the map view is calling both your custom methods and the calloutAccessoryControlTapped delegate method (in which there's no code currently).

In the delegate method, the annotation tapped is accessible via view.annotation.

So in viewForAnnotation, you would just do this:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
    if ([myAnn isKindOfClass:[MKUserLocation class]])
    {
        ((MKUserLocation *)myAnn).title = @"Your position";
        return nil;
    }

    MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView  dequeueReusableAnnotationViewWithIdentifier:@"pinView"];
    if (!pinView) {
        pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:@"pinView"];
        pinView.pinColor = MKPinAnnotationColorRed;
        pinView.canShowCallout = YES;
        pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    }
    else
    {
        pinView.annotation = myAnn;
    }

    return pinView;
}

Then in the calloutAccessoryControlTapped delegate method, you can do something like this:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{

    if ([view.annotation isKindOfClass:[Annotation class]])
    {
        Annotation *myAnn = (Annotation *)view.annotation;

        id vcToPush = nil;

        if ([[myAnn title] isEqualToString:@"Name of the place"]) {
            vcToPush = [[InfoViewController alloc]init];
        }
        else if ([[myAnn title] isEqualToString:@"Name of place two"]) {
            vcToPush = [[InfoTwoController alloc]init];
        }

        [self.navigationController pushViewController:vcToPush animated:YES];
    }
}

Then remove the myAnnClicked and myAnn2Clicked methods.

You would also be much better off creating a generic "Info" view controller instead of a separate one for each annotation.

Some other unrelated things:

  • Don't put a semi-colon at the end of the #define lines
  • You've defined PLACE2_LONGITUDE twice
  • newRegion.center is using PLACE2_LONGITUDE instead of PLACE1_LONGITUDE


来源:https://stackoverflow.com/questions/24702230/callout-opening-wrong-view-after-zoom-in-ios7

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!