问题
I am trying to center an MKMapView
after an annotation was selected. I also have enabled canShowCallout
but it seems that iOS is first displaying the callout (which is shifted when it would not fit in the screen) and then the map is being moved, resulting in the callout being not completely visible on the screen.
How can I center the map BEFORE the callout's position is being rendered and displayed?
回答1:
I wanted to accomplish the same thing and ended up doing the following.
A word of caution before I begin: I know the solution is pretty ugly!...but hey, it works.
Note: I am targeting iOS 9 but it should work on prior versions of iOS:
Okay, here we go:
- first off, create a new property in your view controller, e.g.:
@property(nonatomic, assign, getter=isPinCenteringOngoing) BOOL pinCenteringOngoing;
- in
mapView:viewForAnnotation:
setcanShowCallout
toNO
for your annotationViews in
mapView:didSelectAnnotationView:
do the following:- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { if([view isKindOfClass:$YOURANNOTATIONVIEWCLASS$.class]) { if(!self.isPinCenteringOngoing) { self.pinCenteringOngoing = YES; [self centerMapOnSelectedAnnotationView:($YOURANNOTATIONVIEWCLASS$ *)view]; } else { self.pinCenteringOngoing = NO; } } }
in
mapView:didDeselectAnnotationView:
do the following:- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view { if([view isKindOfClass:$YOURANNOTATIONVIEWCLASS$.class]) { if(!self.isPinCenteringOngoing) { view.canShowCallout = NO; } } }
and finally create a new method that does the actual work:
- (void)centerMapOnSelectedAnnotationView:($YOURANNOTATIONVIEWCLASS$ *)view { // Center map CGPoint annotationCenter = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view.frame)); CLLocationCoordinate2D newCenter = [self.mapView convertPoint:annotationCenter toCoordinateFromView:view.superview]; [self.mapView setCenterCoordinate:newCenter animated:YES]; // Allow callout to be shown view.canShowCallout = YES; // Deselect and then select the annotation so the callout is actually displayed dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) { [self.mapView deselectAnnotation:view.annotation animated:NO]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) { [self.mapView selectAnnotation:view.annotation animated:NO]; }); }); }
To complete my answer, here is a textual explanation of what I'm doing in the code above and why I'm doing it:
- What I want is the annotation to be centered on screen, and the callout to be centered above it.
- What I get by default is:
- When selecting an annotation, the map opens the callout, and if necessary adjusts the map so the callout fits on screen. By no mean does that standard implementation guarantee, that the callout is "centered" above the annotation.
- By centering the map with setCenterCoordinate:, the annotation view is centered on the map.
- Now the two previous points combined can result in the callout to be "cut off" as the annotation is centered on the map, but the callout is not centered above the annotation.
- To fix this, I do the following:
- first I disable the callout to be displayed by default, setting canShowCallout to NO for every annotationView
- when the user selects an annotation, I first center the map
- I then allow the callout to be shown, setting canShowCallout to YES for the selected annotation
- I then deselect and then again select the annotation, so the callout is actually displayed
- in order for the callout to be correctly centered above the annotation, I need to do the deselecting/selecting somewhat delayed so that the map centering can complete
I hope my answer may prove useful.
回答2:
Here an other solution :
Create a new boolean property
var selectFirstAnnotation = false
in your controllerSet it to true before to center the annotation
Add this is in
regionDidChangeAnimated
.func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { if selectFirstAnnotation == true { if let annotation = mapView.annotations.first(where: { !($0 is MKUserLocation) }) { mapView.selectAnnotation(annotation, animated: true) selectFirstAnnotation = false }}}
Works fine for my behaviour
来源:https://stackoverflow.com/questions/34397934/center-mkmapview-before-displaying-callout