iphone - current - mkmapview
¿Cómo puedo crear una animación personalizada "pin-drop" usando MKAnnotationView? (5)
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];
}
Tengo una instancia de MKMapView
y me gustaría utilizar iconos de anotación personalizados en lugar de los iconos 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.
Creo que la mejor opción es establecer ''animatesDrop'' a SÍ. Es una propiedad de MKPinAnnotationView.
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!
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...
}
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 aire 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;
}];
}];
}
}];
}
}