24

我有一个实例MKMapView并且想使用自定义注释图标而不是 MKPinAnnotationView 提供的标准图钉图标。因此,我设置了一个名为 CustomMapAnnotation 的 MKAnnotationView 子类,并重写-(void)drawRect:以绘制 CGImage。这行得通。

当我尝试复制.animatesDropMKPinAnnotationView 提供的功能时,问题就来了;MKMapView当注释被添加到实例时,我希望我的图标逐渐出现,从上往下并以从左到右的顺序出现。

这是 -(void)drawRect: 用于 CustomMapAnnotation,它在您仅绘制 UIImage 时起作用(这是第二行的作用):

- (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 建议,那也很棒。

4

5 回答 5

58

Paul Shapiro 的代码的一个问题是,当您在用户正在查看的位置下方添加注释时,它无法处理。这些注释会在掉落之前漂浮在半空中,因为它们被移动到用户的可见地图矩形中。

另一个是它还删除了用户位置蓝点。使用下面的代码,您可以在屏幕外处理用户位置和大量地图注释。我还添加了一个不错的反弹;)

- (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;
                    }];
                }];
            }
        }];
    }
}
于 2011-08-12T20:24:21.977 回答
52

首先,您需要让您的视图控制器实现 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,当它们被添加时,将调用此委托方法,并在添加引脚时从上到下为引脚设置动画。

时间和定位的值可以稍微改变,但我已经对其进行了调整,使其看起来最好/最接近传统的下降(据我测试)。希望这可以帮助!

于 2010-01-18T15:52:33.397 回答
12

或者,如果您正在制作 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];
  }
于 2010-06-26T18:56:04.363 回答
5

对于 Michael Tyson 的回应(我还不能到处评论),我建议在 didMoveToSuperview 中插入以下代码,以便正确重用 MKAnnotationView 以便它再次执行动画,然后模仿注释的顺序添加

使用除法器和乘法器来获得不同的视觉效果......

- (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...
}
于 2010-11-02T10:08:56.020 回答
0

我认为更好的选择是将'animatesDrop'设置为YES。它是 MKPinAnnotationView 的一个属性。

于 2014-11-10T00:09:15.710 回答