Custom annotation view is being removed from its superview after setting map view center coordinate
I'm working on a location-aware application that is using MKMapView with custom callout bubbles implemented as MKAnnotationView subclass. Yesterday I've encountered a subtle bug when custom annotation view is not being shown. After investigating this issue a bit, I've come up with following results:
- this issue manifests itself only if I'm trying to show custom annotation view immediately after zooming with pinch gesture. So for example if you pinch and then pan the map view everything works fine. Programmatically changing map zoom doesn't cause this issue to appear either
setting breakpoint in didMoveToSuperview in my custom annotation view class reveals the following backtrace:
#0 -[CalloutMapAnnotationView didMoveToSuperview] #1 0x00186857 in -[UIView(Hierarchy) removeFromSuperview] () #2 0x00e14c70 in -[MKAnnotationContainerView _removeAnnotationView:updateCollections:] () #3 0x00e196cb in -[MKAnnotationContainerView _removeAnnotationViews:] () #4 0x00e19f51 in -[MKAnnotationContainerView _displayAnnotationsInMapRect:includePending:animated:removeOffscreenAnnotations:] () #5 0x00e1aaa7 in -[MKAnnotationContainerView _refreshDisplayedAnnotations] () #6 0x00dfc508 in -[MKMapView _didChangeRegionMidstream:centerPoint:] () #7 0x00e0165c in -[MKMapView _goToCenterCoordinate:zoomLevel:animationType:] () #8 0x00df34c3 in -[MKMapView goToCenterCoordinate:zoomLevel:animationType:] () #9 0x00e0086f in -[MKMapView setCenterCoordinate:animated:] () #10 0x00036fc3 in -[CalloutMapAnnotationView adjustMapRegionIfNeeded] #11 0x00037c63 in -[CalloutMapAnnotationView didMoveToSuperview] #12 0x0017f750 in -[UIView(Internal) _addSubview:positioned:relativeTo:] () #13 0x0017dc00 in -[UIView(Hierarchy) insertSubview:atIndex:] () #14 0x00e2049f in -[MKAnnotationContainerView _addViewForAnnotation:] () #15 0x00e199a5 in -[MKAnnotationContainerView _addViewsForAnnotations:animated:] () #16 0x00e19f0d in -[MKAnnotationContainerView _displayAnnotationsInMapRect:includePending:animated:removeOffscreenAnnotations:] () #17 0x00e1a9e2 in -[MKAnnotationContainerView showAddedAnnotationsAnimated:] ()
Here, CalloutMapAnnotationView is my custom annotation view class. adjustMapRegionIfNeeded
method adjusts map view's center coordinate if annotation is too close to map borders which in turn causes removal of CalloutMapAnnotationView instance from its superview. Why this is happening and what a workaround may be?
Further investigation showed even mor开发者_StackOverflow社区e strange behavior. I added a bunch of debug NSLogs in adjustMapRegionIfNeeded
to print each annotation visibility and came up up with following result:
Normal case (custom annotation is displayed):
Custom callout annotation location: (55.821350, 37.497490)
Parent annotation location: (55.821350, 37.497490)
Custom callout annotation visibility before adjustment: 1
Custom callout annotation visibility after adjustment: 1
Parent annotation visibility: 1
Custom annotation is not displayed:
Custom callout annotation location: (55.821350, 37.497490)
Parent annotation location: (55.821350, 37.497490)
Custom callout annotation visibility before adjustment: 1
Custom callout annotation visibility after adjustment: 0
Parent annotation visibility: 1
Despite parent annotation and custom callout annotation having the same location, one of them is visible while the other is not. I'm testing annotation visibility with the following code:
[[self.mapView annotationsInMapRect:self.mapView.visibleMapRect] containsObject:self.annotation]
More than that, the following assertion fails:
MKMapRect visibleMapRect = self.mapView.visibleMapRect;
MKMapPoint annotationPoint = MKMapPointForCoordinate(self.annotation.coordinate);
NSAssert(MKMapRectContainsPoint(visibleMapRect, annotationPoint) == [[self.mapView annotationsInMapRect:visibleMapRect] containsObject:self.annotation], @"?!");
I've come across the same problem (and most probably used the same tutorial that you did).
I've put some NSLogs into the CalloutAnnotationView
's didMoveToSuperView
and found that after adding the CalloutAnnotationView
, its superview, an instance of MKAnnotationContainerView
, is being released. This could mean that the MKMapView
internally recreates its MKAnnotationContainerView
when zooming in or out.
What I did was to do some checking a little bit after adding it:
[self.mapView addAnnotation: m_calloutAnnotation];
[self performSelector: @selector(checkCalloutAnnotationVisibility)
withObject: nil
afterDelay: 0.15];
...
- (void) checkCalloutAnnotationVisibility
{
if (!m_calloutAnnotationView.window)
{
[self.mapView removeAnnotation: m_calloutAnnotation];
[self.mapView addAnnotation: m_calloutAnnotation];
[self performSelector: @selector(checkCalloutAnnotationVisibility)
withObject: nil
afterDelay: 0.1];
}
}
...
- (void) mapView: (MKMapView *) mapView
didDeselectAnnotationView: (MKAnnotationView *) view
{
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector:
@selector(checkCalloutAnnotationVisibility)
object: nil];
...
}
It's pretty hacky, yes. If you find a better solution, please do post. :)
精彩评论