¿Cómo puedo crear una animación personalizada “pin-drop” usando MKAnnotationView?

Tengo una instancia de MKMapView y me gustaría utilizar icons de anotación personalizados en lugar de los icons de pin estándar suministrados por MKPinAnnotationView. Entonces, configuré una subclase de MKAnnotationView llamada CustomMapAnnotation y anulo -(void)drawRect: para dibujar una CGImage. Esto funciona.

El problema surge cuando trato de replicar la funcionalidad .animatesDrop proporcionada por MKPinAnnotationView; Me encantaría que mis íconos aparezcan gradualmente, caídos desde arriba y en orden de izquierda a derecha, cuando las anotaciones se agregan a la instancia de MKMapView .

Aquí está – (void) drawRect: para CustomMapAnnotation, que funciona cuando se dibuja el UIImage (que es lo que hace la 2ª línea):

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

El problema surge cuando agrega el método animateDrop :

 -(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:@""]; } 

Simplemente no funciona, y podría haber muchas razones por las cuales. No voy a entrar en todos ellos ahora. Lo principal que quiero saber es si el enfoque es correcto o si debería intentar algo completamente diferente.

Intenté también empaquetar todo en una transacción de animación para que el parámetro beginTime realmente funcionara; esto parecía no hacer nada en absoluto. No sé si esto se debe a que me falta algún punto clave o si es porque MapKit está destruyendo mis animaciones de alguna manera.

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

Si alguien tiene alguna experiencia con MKMapAnnotations animadas como esta, me encantaría dar algunos consejos; de lo contrario, si puedes ofrecer consejos de CAAnimation sobre el enfoque, sería genial también.

    Un problema con el código de Paul Shapiro es que no se trata de cuándo se agregan las anotaciones a continuación, donde el usuario está mirando el momento. Esas anotaciones flotarán en el air antes de caer porque se moverán al mapa visible del usuario.

    Otra es que también deja caer el punto azul de la ubicación del usuario. Con este código a continuación, maneja la ubicación del usuario y grandes cantidades de anotaciones de mapa fuera de la pantalla. También agregué un buen rebote;)

     - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views { MKAnnotationView *aV; for (aV in views) { // Don't pin drop if annotation is user location if ([aV.annotation isKindOfClass:[MKUserLocation class]]) { continue; } // Check if current annotation is inside visible map rect, else go to next one MKMapPoint point = MKMapPointForCoordinate(aV.annotation.coordinate); if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point)) { continue; } CGRect endFrame = aV.frame; // Move annotation out of view aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - self.view.frame.size.height, aV.frame.size.width, aV.frame.size.height); // Animate drop [UIView animateWithDuration:0.5 delay:0.04*[views indexOfObject:aV] options:UIViewAnimationCurveLinear animations:^{ aV.frame = endFrame; // Animate squash }completion:^(BOOL finished){ if (finished) { [UIView animateWithDuration:0.05 animations:^{ aV.transform = CGAffineTransformMakeScale(1.0, 0.8); }completion:^(BOOL finished){ [UIView animateWithDuration:0.1 animations:^{ aV.transform = CGAffineTransformIdentity; }]; }]; } }]; } } 

    En primer lugar, debe hacer que su controlador de vista implemente MKMapViewDelegate si aún no lo hace.

    Luego, implementa este método:

     - (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]; } } 

    Agregue sus anotaciones a MapView y cuando se agreguen, se llamará a este método de delegado y animará los pines de arriba a abajo a medida que se agreguen.

    Los valores para el tiempo y el posicionamiento se pueden cambiar un poco, pero lo modifiqué para que se vea mejor / más cercano a la caída tradicional (por lo que he probado). ¡Espero que esto ayude!

    Alternativamente, si está haciendo una subclase MKAnnotationView, puede usar didMoveToSuperview para activar la animación. A continuación, aparece una gota que termina en un ligero efecto de “aplastar”

      #define kDropCompressAmount 0.1 @implementation MyAnnotationViewSubclass ... - (void)didMoveToSuperview { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"]; animation.duration = 0.4; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -400, 0)]; animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform"]; animation2.duration = 0.10; animation2.beginTime = animation.duration; animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; animation2.toValue = [NSValue valueWithCATransform3D:CATransform3DScale(CATransform3DMakeTranslation(0, self.layer.frame.size.height*kDropCompressAmount, 0), 1.0, 1.0-kDropCompressAmount, 1.0)]; animation2.fillMode = kCAFillModeForwards; CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"transform"]; animation3.duration = 0.15; animation3.beginTime = animation.duration+animation2.duration; animation3.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; animation3.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; animation3.fillMode = kCAFillModeForwards; CAAnimationGroup *group = [CAAnimationGroup animation]; group.animations = [NSArray arrayWithObjects:animation, animation2, animation3, nil]; group.duration = animation.duration+animation2.duration+animation3.duration; group.fillMode = kCAFillModeForwards; [self.layer addAnimation:group forKey:nil]; } 

    Para la respuesta de Michael Tyson (todavía no puedo comentar en todas partes), propongo una inserción del siguiente código en didMoveToSuperview para una correcta reutilización de MKAnnotationView para que vuelva a hacer la animación y luego imite la adición secuencial de anotaciones

    Juega con los divisores y multiplicadores para diferentes resultados visuales …

     - (void)didMoveToSuperview { //necessary so it doesn't add another animation when moved to superview = nil //and to remove the previous animations if they were not finished! if (!self.superview) { [self.layer removeAllAnimations]; return; } float xOriginDivider = 20.; float pos = 0; UIView *mySuperview = self.superview; while (mySuperview && ![mySuperview isKindOfClass:[MKMapView class]]) mySuperview = mySuperview.superview; if ([mySuperview isKindOfClass:[MKMapView class]]) //given the position in the array // pos = [((MKMapView *) mySuperview).annotations indexOfObject:self.annotation]; // left to right sequence; pos = [((MKMapView *) mySuperview) convertCoordinate:self.annotation.coordinate toPointToView:mySuperview].x / xOriginDivider; float yOffsetMultiplier = 20.; float timeOffsetMultiplier = 0.05; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"]; animation.duration = 0.4 + timeOffsetMultiplier * pos; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -400 - yOffsetMultiplier * pos, 0)]; animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; // rest of animation group... } 

    Creo que la mejor opción es establecer ‘animatesDrop’ a SÍ. Es una propiedad de MKPinAnnotationView.