Question

After calling MKMapView's setCenterCoordinate:animated: method (without animation), I'd like to call selectAnnotation:animated: (with animation) so that the annotation pops out from the newly-centered pushpin.

For now, I simply watch for mapViewDidFinishLoadingMap: and then select the annotation. However, this is problematic. For instance, this method isn't called when there's no need to load additional map data. In those cases, my annotation isn't selected. :(

Very well. I could call this immediately after setting the center coordinate instead. Ahh, but in that case it's possible that there is map data to load (but it hasn't finished loading yet). I'd risk calling it too soon, with the animation becoming spotty at best.

Thus, if I understand correctly, it's not a matter of knowing if my coordinate is visible, since it's possible to stray almost a screenful of distance and have to load new map data. Rather, it's a matter of knowing if new map data needs to be loaded, and then acting accordingly.

Any ideas on how to accomplish this, or how to otherwise (reliably) select an annotation after re-centering the map view on the coordinate where that annotation lives?

Clues appreciated - thanks!

Was it helpful?

Solution

I ran into the same problem, but found what seems like a reliable and reasonable solution:

  1. Implement the delegate method mapView:didAddAnnotationViews:. When I tried selecting the annotation directly within the delegate method, the callout dropped with the pin! That looked odd, so I add a slight delay of a half-second.

    -(void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
        [self performSelector:@selector(selectInitialAnnotation)
              withObject:nil afterDelay:0.5];
    }
    
  2. Select the initial annotation as you'd expect, but calling selectAnnotation:animated;

    -(void)selectInitialAnnotation {
        [self.mapView selectAnnotation:self.initialAnnotation animated:YES];
    }
    

It seems that selectAnnotation:animated: is not called under some conditions. Compare with MKMapView docs:

If the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.

OTHER TIPS

A more consistent way than using a fixed timeout is to listen to the regionDidChange callback. Set the center coordinate of the map to the desired annotation, and when the regionDidChange method is called, then select the annotation in the center, to open the callout.

Here's a little video I took of the thing running randomly between 5 pins.

First, goto the center coordinate of the annotation. Let's say the annotation object is named thePin.

- (void)someMethod {
    [map setCenterCoordinate:thePin.coordinate animated:YES];
}

Then in the regionDidChange method, select this annotation.

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    [map selectAnnotation:thePin animated:YES];
}

Well just FYI, this is what the docs say,

If the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.

So if I want to call setCenterCoordinate:animated: or setRegion:animated: and then I want to select the annotation by calling, selectAnnotation:animated: , the annotation won't get selected and the callout won't appear beacause of the exact same reason mentioned above in docs, So the way it would be great to have something like, setCenterCoordinate:animated:ComletionBlock but its not there..! The way that worked for me is as below,

 [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.mapView setCenterCoordinate:location.coordinate animated:YES];
    } completion:^(BOOL finished) {
        [self.mapView selectAnnotation:location animated:YES];
    }];

This will give you a completion block and u can use that to select the annotation.

What has worked for me was calling selectAnnotation:animated: from the mapView:didAddAnnotationViews: method:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views;
{
    [mapView selectAnnotation:[[mapView annotations] lastObject] animated:YES];
}

Apple documentation on the same here.

Note that I only had one annotation on the map so [[mapView annotations] lastObject] was fine for my purposes. Your mileage may vary.

I was having a similar problem. I was using the default MKPinAnnotationView with animatesDrop:YES. After I added the annotations to the MKMapView, I was doing this:

[mapView selectAnnotation:[mapView.annotations objectAtIndex:1] animated:YES]

which in the logic of my program, should select the nearest annotation. This wasn't working. I figured out the reason: the annotation view was not on the screen at the time of this select call, because of the pin drop animation. So all I did was set a timer to select the annotation a second later. It's a hack, but it works. I'm not sure if it'll work in every situation though, for instance on a 3G vs. 3Gs. It'd be better to figure out the right callback function to put it in.

selectTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
               selector:@selector(selectClosestAnnotation) userInfo:nil
               repeats:NO];

- (void)selectClosestAnnotation {
    [mapView selectAnnotation:[mapView.annotations objectAtIndex:1]
             animated:YES];
}

I've found several problems with all the solutions I saw for my problem (I want to select a annotation when I added it from viewDidAppear):

  1. Using the mapView:didAddAnnotationViews: delegate method.

    This didn't work for me because if the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.

  2. Using the mapViewDidFinishLoadingMap: delegate method.

    This didn't work for me either because the map is cached now, so the method is called only once, the first time.

Solution:

John Blackburn was very close but without the mapView:didAddAnnotationViews: method. I just call my own selectAnnotation: method, before I add the annotation, with a delay:

[self.mapView addAnnotation:ann];
[self performSelector:@selector(selectAnnotation:) withObject:ann afterDelay:0.5];

And this is what I do in my selectAnnotation: method:

- (void)selectAnnotation:(id < MKAnnotation >)annotation
{
   [self.mapView selectAnnotation:annotation animated:YES];
}

Tested on two iPhone 3GS (iOS 5 and 6) and an iPhone 5.

I was having similar difficulty, but was actually not able to make this method work at all, let alone inconsistently.

Here is what I found to work. Maybe it will work for you too: How to trigger MKAnnotationView's callout view without touching the pin?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top