web-dev-qa-db-ja.com

MKAnnotationViewを使用してカスタムの「ピンドロップ」アニメーションを作成するにはどうすればよいですか?

MKMapViewのインスタンスがあり、MKPinAnnotationViewによって提供される標準のピンアイコンの代わりにカスタム注釈アイコンを使用したいと思います。そこで、CustomMapAnnotationというMKAnnotationViewのサブクラスを設定し、-(void)drawRect:をオーバーライドしてCGImageを描画しています。これは機能します。

MKPinAnnotationViewによって提供される.animatesDrop機能を複製しようとすると問題が発生します。注釈がMKMapViewインスタンスに追加されたときに、アイコンが上から左から右の順序で徐々に表示されるようにしたいと思います。

次に-(void)drawRect:CustomMapAnnotationの場合、UIImageを描画するだけで機能します(これは2行目で行われます)。

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

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

それはうまくいかず、多くの理由が考えられます。今はそれらすべてに入るわけではありません。私が知りたい主なことは、アプローチがまったく健全であるかどうか、またはまったく異なることを試みる必要があるかどうかです。

また、beginTimeパラメーターが実際に機能するように、すべてをアニメーショントランザクションにパッケージ化しようとしました。これはまったく何もしないようでした。これが重要なポイントを見逃しているためなのか、それともMapKitがアニメーションを何らかの形で破壊しているためなのかはわかりません。

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

このようなアニメーション化されたMKMapAnnotationsの経験がある人がいれば、いくつかのヒントが欲しいです。そうでなければ、アプローチに関するCAAnimationのアドバイスを提供できれば、それも素晴らしいことです。

24
user226010

Paul Shapiroによるコードの問題の1つは、ユーザーが現在見ている場所の下に注釈を追加すると、コードが処理されないことです。これらの注釈は、ユーザーの表示可能なマップrectに移動されるため、ドロップする前に空中に浮きます。

もう1つは、ユーザーの場所の青い点も削除することです。以下のこのコードを使用すると、ユーザーの場所と大量の地図注釈の両方を画面外で処理できます。ニースバウンスも追加しました;)

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

まず、View ControllerにMKMapViewDelegateがまだ実装されていない場合は、実装させる必要があります。

次に、このメソッドを実装します。

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

   }
}

注釈をMapViewに追加すると、注釈が追加されると、このデリゲートメソッドが呼び出され、追加されるときにピンが上から下にアニメーション化されます。

タイミングと位置の値は少し変更できますが、(テストした限りでは)従来のドロップに最もよく/最も近く見えるように調整しました。お役に立てれば!

52
Paul Shapiro

または、MKAnnotationViewサブクラスを作成している場合は、didMoveToSuperviewを使用してアニメーションをトリガーできます。以下は、わずかな「スキッシュ」効果で終わるドロップを行います

  #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];
  }
12
Michael Tyson

Michael Tysonの応答(まだどこにもコメントできません)については、MKAnnotationViewを適切に再利用するために、didMoveToSuperviewに次のコードを挿入して、アニメーションを再度実行し、注釈の順次追加を模倣することを提案します。

さまざまな視覚的結果を得るために、仕切りと乗数を試してみてください...

- (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...
}
5
tsakoyan

'animatesDrop'をYESに設定するのがより良いオプションだと思います。これはMKPinAnnotationViewのプロパティです。

0
Chris