如何解决导致重复标注的 MapKit 错误?

Posted

技术标签:

【中文标题】如何解决导致重复标注的 MapKit 错误?【英文标题】:How can I work around this MapKit bug that causes duplicate callouts? 【发布时间】:2015-01-16 23:06:47 【问题描述】:

有一个bug in MapKit that can cause duplicate callout views on an annotation。如果时机恰到好处,注释视图可以在它被选中时被重新使用,显然就在标注视图被实际添加到它之前。结果,旧的标注视图被卡在那里,而新的标注将出现在它的顶部或旁边。这是在 OS X 应用程序中的样子:

这张地图上只有一个注释。如果您单击地图上的其他位置以取消选择注记,则只有一个标注会消失。在某些情况下,您可能有两个信息完全不同的标注,这会让使用您的应用的人感到非常困惑。

这是我整理的一个示例 OS X 项目的大部分内容,用于说明此错误:

@import MapKit;
#import "AppDelegate.h"
#import "JUNMapAnnotation.h"

@interface AppDelegate () <MKMapViewDelegate>

@property (weak) IBOutlet NSWindow *window;
@property (weak) IBOutlet MKMapView *mapView;
@property BOOL firstPin;

- (void)placeAndSelectPin;
- (JUNMapAnnotation *)placePin;
- (void)clearPins;

@end

@implementation AppDelegate

- (IBAction)dropSomePins:(id)sender 
    self.firstPin = YES;
    [self placeAndSelectPin];
    [self performSelector:@selector(placeAndSelectPin) withObject:nil afterDelay:0.0001];


#pragma mark - Private methods

- (void)placeAndSelectPin 
    [self clearPins];
    JUNMapAnnotation *annotation = [self placePin];
    [self.mapView deselectAnnotation:annotation animated:NO];
    [self.mapView selectAnnotation:annotation animated:YES];


- (JUNMapAnnotation *)placePin 
    CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(50.0,50.0);
    JUNMapAnnotation *annotation = [[JUNMapAnnotation alloc] initWithCoordinate:coord];
    annotation.title = @"Annotation";
    annotation.subtitle = (self.firstPin) ? @"This is an annotation with a longer subtitle" : @"This is an annotation";
    [self.mapView addAnnotation:annotation];
    self.firstPin = NO;
    return annotation;


- (void)clearPins 
    [self.mapView removeAnnotations:self.mapView.annotations];


#pragma mark - MKMapViewDelegate

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation 
    if ([annotation isKindOfClass:[JUNMapAnnotation class]]) 

        static NSString *identifier = @"annotationView";
        MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (view == nil) 
            view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            view.canShowCallout = YES;
            NSLog(@"new annotation view");
         else 
            view.annotation = annotation;
        
        return view;

    
    return nil;


@end

ios 中似乎也存在同样的错误,尽管我在那里重新创建它时遇到了困难。

虽然我正在等待 Apple 解决此问题,但我想尽可能地解决它。到目前为止,我提出了一些可能性:

    不要重复使用注释视图。据我所知,这似乎是完全避免该错误的唯一方法,但似乎效率很低。 当注释视图在mapView:viewForAnnotation: 中重复使用时,删除其所有子视图。目前,标注似乎是唯一的子视图,尽管它似乎不是一个特别安全的黑客。它也只是一种工作方式——它不会阻止重复标注的出现,它只是防止它们永远存在。 (当这个错误第一次发生时,实际上还没有任何子视图。) 结合这两者:如果dequeueReusableAnnotationViewWithIdentifier: 返回一个包含任何子视图的视图,忽略它并创建一个新视图。这似乎比 2 安全得多,而且效率也不像 1 那样低。但与 2 一样,它并不是一个完整的解决方法。

我还尝试在我能想到的每个地方添加deselectAnnotation:animated:,但我找不到任何可行的方法。我假设一旦注释视图被重新使用,MapView 就会失去对第一个标注的跟踪,因此它的任何常规方法都不会摆脱它。

【问题讨论】:

【参考方案1】:

这有点超出了左侧字段,但是.. 尝试使用 2 个不同的重用标识符注册相同的单元类。在viewForAnnotation: 中,在出列单元格时交替使用每个标识符。这应该可以防止连续两次从同一个队列中抓取。

【讨论】:

有趣的是,这在我的 OS X 测试应用程序中有效,以前我每次都能重新创建错误。但后来我在 iOS 上尝试了同样的事情,我能够立即重新创建错误。真可惜。好主意! viewForAnnotation: 可能在不同的线程上被调用。您可能想尝试将 @synchronized(self) 包裹在方法中的所有内容上。

以上是关于如何解决导致重复标注的 MapKit 错误?的主要内容,如果未能解决你的问题,请参考以下文章

根据 MapKit 注释标注在视图中加载数据单击

Mapkit,如何检测注释已加载

如何阻止 MKClusterAnnotation 显示标注

如何添加在图形页面定制标注视图

如何将信息从 MapKit 注释发送到新的视图控制器

如何在地图视图中添加自定义标注视图