MKAnnotationView拖动状态结束动画

时间:2014-08-15 09:33:20

标签: ios mapkit uiviewanimation mkannotationview

我正在使用Daniel提供的自定义MKAnnotationView动画:Subclassing MKAnnotationView and overriding setDragState但我遇到了一个问题。

在引脚放置动画之后,当我去移动地图时,mkannotationview会在调用最终引脚放置动画块之前跳回到之前的位置。

在我看来,在动画运行之前调用了dragState = MKAnnotationViewDragStateEnding?如何绕过这个问题并将mkannotationview的最终点设置为动画结束时的点?

#import "MapPin.h"

NSString *const DPAnnotationViewDidFinishDrag = @"DPAnnotationViewDidFinishDrag";
NSString *const DPAnnotationViewKey = @"DPAnnotationView";

// Estimate a finger size
// This is the amount of pixels I consider
// that the finger will block when the user
// is dragging the pin.
// We will use this to lift the pin even higher during dragging

#define kFingerSize 20.0

@interface MapPin()
@property (nonatomic) CGPoint fingerPoint;
@end

@implementation MapPin
@synthesize dragState, fingerPoint, mapView;

- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    if(mapView){
        id<MKMapViewDelegate> mapDelegate = (id<MKMapViewDelegate>)mapView.delegate;
        [mapDelegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState];
    }

    // Calculate how much to life the pin, so that it's over the finger, no under.
    CGFloat liftValue = -(fingerPoint.y - self.frame.size.height - kFingerSize);

    if (newDragState == MKAnnotationViewDragStateStarting)
    {
        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-liftValue);
        [MapPin animateWithDuration:0.2
                         animations:^{
                             self.center = endPoint;
                         }
                         completion:^(BOOL finished){
                             dragState = MKAnnotationViewDragStateDragging;
                         }];

    }
    else if (newDragState == MKAnnotationViewDragStateEnding)
    {
        // lift the pin again, and drop it to current placement with faster animation.

        __block CGPoint endPoint = CGPointMake(self.center.x,self.center.y-liftValue);
        [MapPin animateWithDuration:0.2
                         animations:^{
                             self.center = endPoint;
                         }
                         completion:^(BOOL finished){
                             endPoint = CGPointMake(self.center.x,self.center.y+liftValue);
                             [MapPin animateWithDuration:0.1
                                              animations:^{
                                                  self.center = endPoint;
                                              }
                                              completion:^(BOOL finished){
                                                  dragState = MKAnnotationViewDragStateNone;
                                                  if(!mapView)
                                                      [[NSNotificationCenter defaultCenter] postNotificationName:DPAnnotationViewDidFinishDrag object:nil userInfo:[NSDictionary dictionaryWithObject:self.annotation forKey:DPAnnotationViewKey]];
                                              }];
                         }];
    }
    else if (newDragState == MKAnnotationViewDragStateCanceling)
    {
        // drop the pin and set the state to none

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+liftValue);
        [UIView animateWithDuration:0.2
                         animations:^{
                             self.center = endPoint;
                         }
                         completion:^(BOOL finished){
                             dragState = MKAnnotationViewDragStateNone;
                         }];
    }
}

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    // When the user touches the view, we need his point so we can calculate by how
    // much we should life the annotation, this is so that we don't hide any part of
    // the pin when the finger is down.

    fingerPoint = point;
    return [super hitTest:point withEvent:event];
}

@end

1 个答案:

答案 0 :(得分:7)

我遇到了同样的问题,特别是在iOS 8下。经过数小时的测试后,我相信iOS会在状态为self.center期间跟踪注释的MKAnnotationViewDragStateDragging。 。如果您在处理self.center时动画MKAnnotationViewDragStateEnding,则需要格外小心。读一下,“我无法让它工作,永远。”

相反,在处理状态MKAnnotationViewDragStateStartingMKAnnotationViewDragStateCanceling时,我保留了 Daniel 的原始代码,我动画了self.center。在处理MKAnnotationViewDragStateEnding时,我动画了self.transform而不是self.center。这样可以保持注释的实际位置,只是更改它的呈现方式。

这适用于我运行iOS 7.1和iOS 8.0。还修复了hitTest中的错误,并添加了一些代码以在拖动或取消后重新选择注释。我认为这是MKPinAnnotationView的默认行为。

- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    if(mapView){
        id<MKMapViewDelegate> mapDelegate = (id<MKMapViewDelegate>)mapView.delegate;
        [mapDelegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState];
    }

    // Calculate how much to lift the pin, so that it's over the finger, not under.
    CGFloat liftValue = -(fingerPoint.y - self.frame.size.height - kFingerSize);

    if (newDragState == MKAnnotationViewDragStateStarting)
    {
        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-liftValue);
        [UIView animateWithDuration:0.2
                         animations:^{
                             self.center = endPoint;
                         }
                         completion:^(BOOL finished){
                             dragState = MKAnnotationViewDragStateDragging;
                         }];

    }
    else if (newDragState == MKAnnotationViewDragStateEnding)
    {
        CGAffineTransform theTransform = CGAffineTransformMakeTranslation(0, -liftValue);
        [UIView animateWithDuration:0.2
                         animations:^{
                             self.transform = theTransform;
                         }
                         completion:^(BOOL finished){
                             CGAffineTransform theTransform2 = CGAffineTransformMakeTranslation(0, 0);
                             [UIView animateWithDuration:0.2
                                              animations:^{
                                                  self.transform = theTransform2;
                                              }
                                              completion:^(BOOL finished){
                                                  dragState = MKAnnotationViewDragStateNone;
                                                  if(!mapView)
                                                      [[NSNotificationCenter defaultCenter] postNotificationName:DPAnnotationViewDidFinishDrag object:nil userInfo:[NSDictionary dictionaryWithObject:self.annotation forKey:DPAnnotationViewKey]];
                                                  // Added this to select the annotation after dragging.
                                                  // This is the behavior for MKPinAnnotationView
                                                  if (mapView)
                                                      [mapView selectAnnotation:self.annotation animated:YES];
                                              }];
                         }];
    }
    else if (newDragState == MKAnnotationViewDragStateCanceling)
    {
        // drop the pin and set the state to none
        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+liftValue);

        [UIView animateWithDuration:0.2
                         animations:^{
                             self.center = endPoint;
                         }
                         completion:^(BOOL finished){
                             dragState = MKAnnotationViewDragStateNone;
                             // Added this to select the annotation after canceling.
                             // This is the behavior for MKPinAnnotationView
                             if (mapView)
                                 [mapView selectAnnotation:self.annotation animated:YES];
                         }];
    }
}

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    // When the user touches the view, we need his point so we can calculate by how 
    // much we should life the annotation, this is so that we don't hide any part of
    // the pin when the finger is down.

    // Fixed a bug here.  If a touch happened while the annotation view was being dragged
    // then it screwed up the animation when the annotation was dropped.
    if (dragState == MKAnnotationViewDragStateNone)
    {
        fingerPoint = point;
    }
    return [super hitTest:point withEvent:event];
}