Wanted: How to Reliably, Consistently Select an Mkmapview Annotation

Wanted: How to reliably, consistently select an MKMapView annotation

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.

Why does selectAnnotation not work in this scenario?

The annotation view has to have been added before you can select the annotation.

Please see this popular question:

How to trigger MKAnnotationView's callout view without touching the pin?

How to trigger MKAnnotationView's callout view without touching the pin?

Ok, here's the solution to this problem.

To display the callout use MKMapView's selectAnnotation:animated method.

ios MapKit have annotation description visible on launch and not on tap

use an overlay instead of an annotation

How to use MKMapView finished loading delegate, possible finished displaying delegate?

Update: iOS7 has a new delegate which may have fixed this problem. I have not confirmed one way or the other yet.

- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered 

Pre iOS6 support:

mapViewDidFinishLoadingMap: appears to be unreliable. I notice that it is sometimes not called at all, especially if the map tiles are already cached, and sometimes it is called multiple times.

I notice that when it is called multiple times the last call will render correctly. So I think you can get this to work if you set up a 2 second timer after the user taps save. Disable interactions so that nothing else can happen, and enable user interactions when the timer goes.

If mapViewDidFinishLoadingMap gets called reset the timer again for 2 seconds in the future. When the timer finally goes off, get the snapshot of the map and it should be correct.

You will also want to consider the other callbacks such as mapViewDidFailLoadingMap. Also test this on a noisy connection, since 2 seconds may not be long enough if it takes a long time to fetch the tiles.

- (void)restartTimer
{
[self.finishLoadingTimer invalidate];
self.finishLoadingTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(mapLoadingIsFinished)
userInfo:nil
repeats:NO];
}

- (void)userClickedSave
{
assert(self.saving == NO);
if (self.saving == NO) {
self.saving = YES;
assert(self.finishLoadingTimer == nil);
self.view.userInteractionEnabled = NO;
[self restartTimer];
}
}

- (void)mapLoadingIsFinished
{
self.finishLoadingTimer = nil;
[self doSnapshotSequence];
self.saving = NO;
self.view.userInteractionEnabled = YES;
}

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
if (self.saving) {
[self restartTimer];
}
}

Handling overlapping MKAnnotationView(s)

I ended up working with zPosition:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"tag" ascending:NO];
NSArray *sortedViews = [views sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

// Set zPosition
for (MKAnnotationView *annotationView in sortedViews) {
annotationView.layer.zPosition = ++_zPositionForAnnotationViews;
}

// Do whatever
// . . .
}

- (void)mapView:(MKMapView *)aMapView didSelectAnnotationView:(MKAnnotationView *)view {
NSLog(@"The annotation tapped is: %@", view.annotation.title);

// Ignore tap on user location
if ([view.annotation isKindOfClass:[MKUserLocation class]]) {
return;
}

// Update zPosition (to bring it to front)
view.layer.zPosition = ++_zPositionForAnnotationViews;

// Do whatever
// . . .
}

Where _zPositionForAnnotationViews is a class variable.

Choose current annotation

To get the current annotations on the map you could loop like this:

MKMapView * map = [[MKMapView alloc]initWithFrame:self.view.frame];

for (MKPointAnnotation * annotation in [map annotations])
{
//Change annotation coordinate to new coordinate
}

Set first annotation in annotations

Howe said @ogres: mapView might re-arrange the array of the Pubs, so i had to select it from the TableView-s array mapView.selectAnnotation(tableView.annotations.first!, animated: true)

Mapkit Annotation type when zooming in and out?

Hey veer the problem is that this method is called if the user pans the map to view another location and then comes back to the place where the annotations are plotted.

- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation

I have seen many sample code for map application and this in what most of the people are using.

- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString* AnnotationIdentifier = @"AnnotationIdentifier";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if(annotationView)
return annotationView;
else
{
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.draggable = YES;
pinView.pinColor = MKPinAnnotationColorGreen;
return pinView;
}
return nil;
}


Related Topics



Leave a reply



Submit