2009-12-07 14 views
23

Ho un'istanza di MKMapView e vorrei utilizzare icone di annotazione personalizzate al posto delle icone di pin standard fornite da MKPinAnnotationView. Quindi, ho creato una sottoclasse di MKAnnotationView chiamata CustomMapAnnotation e sto sostituendo -(void)drawRect: per disegnare un CGImage. Questo funziona.Come posso creare un'animazione "pin-drop" personalizzata usando MKAnnotationView?

Il problema si presenta quando provo a replicare la funzionalità .animatesDrop fornita da MKPinAnnotationView; Mi piacerebbe che le mie icone appaiano gradualmente, cadute dall'alto e nell'ordine da sinistra a destra, quando le annotazioni vengono aggiunte all'istanza MKMapView.

Ecco - (void) drawRect: per CustomMapAnnotation, che funziona solo quando si disegna l'UIImage (che è ciò che il 2 ° riga fa):

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

Il guaio arriva quando si aggiunge il metodo 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:@""]; 
} 

Semplicemente non funziona, e potrebbero esserci molti motivi per cui. Non voglio entrare in tutti loro ora. La cosa principale che voglio sapere è se l'approccio è corretto, o se dovrei provare qualcosa di completamente diverso.

Ho provato anche a impacchettare il tutto in una transazione di animazione in modo che il parametro beginTime potesse effettivamente funzionare; questo sembrava non fare nulla. Non so se questo è perché mi manca qualche punto chiave o se è perché MapKit sta distruggendo le mie animazioni in qualche modo.

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

Se qualcuno ha qualche esperienza con MKMapAnnotations animati come questo, mi piacerebbe alcuni suggerimenti, altrimenti se si può offrire consigli CAAnimation sull'approccio, che sarebbe troppo grande.

risposta

52

In primo luogo, è necessario rendere il controller di visualizzazione implementare MKMapViewDelegate se non lo è già.

Poi, implementare questo metodo:

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

    } 
} 

Aggiungi annotazioni al MapView e quando vengono aggiunti, questo metodo delegato sarà chiamato e animerà i pin da cima a fondo come sono aggiunti.

I valori per la sincronizzazione e il posizionamento possono essere leggermente modificati, ma l'ho ottimizzato per renderlo migliore/più vicino alla caduta tradizionale (per quanto ho provato). Spero che questo ti aiuti!

+0

Grazie ~ questo funziona per me – ludo

+0

Puoi aiutarmi nel mio problema .. Devo mostrare 4 righe di dati in quella bolla ... Come un titolo ... quindi descrizione, data, nome della posizione .. Tutti gli esempi mostrando solo 2 linee di dettagli. Ho provato ad aggiungere dati ricercati nel sottotitolo e separati da un carattere \ n. Ma non ha funzionato .. Plz help me –

+0

Sijo, dai un'occhiata a questo. http://stackoverflow.com/questions/1350050/custom-mkannotationview-callout –

12

In alternativa, se si sta creando una sottoclasse MKAnnotationView, è possibile utilizzare didMoveToSuperview per attivare l'animazione. Quanto segue fa una goccia che termina con un leggero effetto 'squish'

#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]; 
    } 
5

la risposta di Michael Tyson (non posso commentare in tutto il mondo ancora), vi propongo un inserimento del seguente codice nel didMoveToSuperview per il corretto riutilizzo dei MKAnnotationView così fa di nuovo l'animazione e quindi imita l'aggiunta sequenziale di annotazioni

Gioca con i divisori e moltiplicatori per diversi risultati visivi ...

- (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... 
} 
+1

per la mia app, il primo blocco di codice qui è fondamentale in iOS 6, ma non nelle versioni precedenti. Grazie. – Keith

58

Un problema con il codice di Paul Shapiro è che non si tratta di quando si aggiungono annotazioni sotto dove l'utente sta guardando in quel momento. Quelle annotazioni galleggeranno a mezz'aria prima di cadere perché vengono spostate nel rect della mappa visibile dell'utente.

Un altro è che lascia cadere anche il punto blu della posizione dell'utente. Con questo codice qui sotto, gestisci sia la posizione dell'utente che grandi quantità di annotazioni della mappa fuori dallo schermo. Ho anche aggiunto un bel rimbalzo;)

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

Questa risposta è semplicemente fantastica. Fatti un favore e prendi in considerazione l'utilizzo di questo pezzo di codice, specialmente se ti piacciono le animazioni super cool come nell'app della mappa. – xci

+5

Cose fantastiche. Un miglioramento: cambia la trasformazione di zucca animata di 'CGAffineTransformMakeScale' in' aV.transform = CGAffineTransformMake (1.0, 0, 0, 0.8, 0, + aV.frame.size.height * 0.1); 'questo renderà così la vista di annotazione schiaccia verso il basso nel suo bordo inferiore (prima di rimbalzare indietro), piuttosto che schiacciare uniformemente al centro. –

+0

@WilliamDenniss grazie per il miglioramento! È un bel tocco –

0

Penso che l'opzione migliore sia impostare 'animatesDrop' su SÌ. È una proprietà di MKPinAnnotationView.

+0

Scatterà il pin animato una volta sulla mappa. non si animerà a caso. –

Problemi correlati