MKAnnotationView拖动状态结束动画



我使用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

我也遇到了同样的问题,特别是在iOS 8下。经过几个小时的测试,我相信iOS会在状态为MKAnnotationViewDragStateDragging时跟踪它认为注释的self.center所在的位置。当处理MKAnnotationViewDragStateEnding时,如果你动画self.center,你需要非常小心。你可以把这句话理解为"我永远无法做到这一点。"

相反,在处理状态MKAnnotationViewDragStateStartingMKAnnotationViewDragStateCanceling时,我保留了Daniel的原始代码,我动画了self.center。当处理MKAnnotationViewDragStateEnding时,我动画self.transform而不是self.center。这将维护注释的实际位置,并更改其呈现方式。

这对我运行iOS 7.1和iOS 8.0都很有效。还修复了hitTest中的一个bug,并添加了一些代码来在拖动或取消注释后重新选择注释。我想这是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];
}

最新更新