How can I create a custom "pin-drop" animation using MKAnnotationView?

Viewed 30963

I have an instance of MKMapView and would like to use custom annotation icons instead of the standard pin icons supplied by MKPinAnnotationView. So, I've setup a subclass of MKAnnotationView called CustomMapAnnotation and am overriding -(void)drawRect: to draw a CGImage. This works.

The trouble comes when I try to replicate the .animatesDrop functionality supplied by MKPinAnnotationView; I would love for my icons to appear gradually, dropped from above and in left-to-right order, when the annotations are added to the MKMapView instance.

Here is -(void)drawRect: for CustomMapAnnotation, which works when you just draw the UIImage (which is what the 2nd line does):

- (void)drawRect:(CGRect)rect {
 [super drawRect:rect];
 [((Incident *)self.annotation).smallIcon drawInRect:rect];
 if (newAnnotation) {
  [self animateDrop];
  newAnnotation = NO;
 }
} 

The trouble comes when you add the animateDrop method:

-(void)animateDrop {
 CGContextRef myContext = UIGraphicsGetCurrentContext();

 CGPoint finalPos = self.center;
 CGPoint startPos = CGPointMake(self.center.x, self.center.y-480.0);
 self.layer.position = startPos;

 CABasicAnimation *theAnimation;
 theAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
 theAnimation.fromValue=[NSValue valueWithCGPoint:startPos];
 theAnimation.toValue=[NSValue valueWithCGPoint:finalPos];
 theAnimation.removedOnCompletion = NO;
 theAnimation.fillMode = kCAFillModeForwards;
 theAnimation.delegate = self;
 theAnimation.beginTime = 5.0 * (self.center.x/320.0);
 theAnimation.duration = 1.0;
 [self.layer addAnimation:theAnimation forKey:@""];
}

It just doesn't work, and there could be a lot of reasons why. I won't get into all of them now. The main thing I am wanting to know is if the approach is sound at all, or if I should try something entirely different.

I tried also to package up the whole thing into an animation transaction so that the beginTime parameter might actually work; this seemed to not do anything at all. I don't know if this is because I am missing some key point or whether it's because MapKit is trashing my animations somehow.

  // Does nothing
  [CATransaction begin];
  [map addAnnotations:list];
  [CATransaction commit];

If anyone has any experience with animated MKMapAnnotations like this, I'd love some hints, otherwise if you can offer CAAnimation advice on the approach, that'd be great too.

5 Answers

First, you need to make your view controller implement MKMapViewDelegate if it doesn't already.

Then, implement this method:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views { 
   MKAnnotationView *aV; 
   for (aV in views) {
     CGRect endFrame = aV.frame;

     aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - 230.0, aV.frame.size.width, aV.frame.size.height);

     [UIView beginAnimations:nil context:NULL];
     [UIView setAnimationDuration:0.45];
     [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
         [aV setFrame:endFrame];
     [UIView commitAnimations];

   }
}

Add your annotations to the MapView and when they are added, this delegate method will be called and will animate the pins from top to bottom as they are added.

The values for timing and positioning can be changed a little bit but I've tweaked it to make it look best/closest to the traditional drop (as far as I've tested). Hope this helps!

Related