【问题标题】:How can I create a custom "pin-drop" animation using MKAnnotationView?如何使用 MKAnnotationView 创建自定义“pin-drop”动画?
【发布时间】:2010-12-23 20:25:24
【问题描述】:

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

当我尝试复制 MKPinAnnotationView 提供的.animatesDrop 功能时,问题就来了;当注释添加到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 建议,那也很棒。

【问题讨论】:

标签: iphone core-animation mapkit mkpinannotationview


【解决方案1】:

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

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

【讨论】:

  • 你能帮我解决我的问题吗..我必须在那个气泡中显示 4 行数据..像一个标题..然后描述、日期、位置名称..所有示例仅显示 2细节线。我试着像在字幕中附加想要的数据并用 \n 字符分隔..但它没有用..请帮助我
  • 如果你迭代了一个浮点数,你可以通过执行以下操作来调整开始时间: [UIView setAnimationDelay:offset] 以便获得顺序动画。
【解决方案2】:

或者,如果您正在制作 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];
  }

【讨论】:

    【解决方案3】:

    对于 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...
    }
    

    【讨论】:

    • 对于我的应用程序,这里的第一段代码在 iOS 6 中至关重要,但在以前的版本中不是。谢谢。
    【解决方案4】:

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

    【讨论】:

    • 这个答案简直太棒了。帮自己一个忙,考虑使用这段代码,特别是如果您喜欢地图应用中的超酷动画。
    • 很棒的东西。一项改进:将CGAffineTransformMakeScale animate squash transform 更改为 aV.transform = CGAffineTransformMake(1.0, 0, 0, 0.8, 0, + aV.frame.size.height*0.1); 这将使注释视图向下挤压到其底部边缘(在反弹之前),而不是均匀挤压到中心。
    • @WilliamDenniss 感谢您的改进!这是一个非常好的触摸。
    • 太棒了,它为研究那些仿射变换提供了很大的动力。谢谢!
    【解决方案5】:

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

    【讨论】:

    • 它会在地图上放置一次动画图钉。它不会随机动画。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多