MKAnnotationView拖动状态结束动画

afc*_*old 1 mapkit mkannotationview uiviewanimation ios

我正在使用Daniel提供的自定义MKAnnotationView动画:子类化MKAnnotationView并覆盖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
Run Code Online (Sandbox Code Playgroud)

小智 7

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

相反,我在处理状态时保留了丹尼尔的原始代码,MKAnnotationViewDragStateStarting并且MKAnnotationViewDragStateCanceling我动画了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];
}
Run Code Online (Sandbox Code Playgroud)