子类化MKAnnotationView并覆盖setDragState

时间:2010-09-09 13:49:08

标签: iphone cocoa-touch ios4 mapkit

这是关于使用MKMapKit的iPhone应用程序:

我为可拖动的注释创建了一个自定义MKAnnotationView。我想创建一个自定义动画。我设置了一个自定义图钉图像并且注释是可拖动的(这里没有显示,它发生在mapview中),代码如下:

- (void) movePinUpFinished {

     [super setDragState:MKAnnotationViewDragStateDragging];
     [self setDragState:MKAnnotationViewDragStateDragging];
}

- (void) setDragState:(MKAnnotationViewDragState) myState {
     if (myState == MKAnnotationViewDragStateStarting) {
          NSLog(@"starting");
          CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
          self.center = endPoint;
          [self movePinUpFinished];
     }
     if (myState == MKAnnotationViewDragStateEnding) {
          NSLog(@"ending");
          [super setDragState:MKAnnotationViewDragStateEnding];
          [self setDragState:MKAnnotationViewDragStateNone];
          [super setDragState:MKAnnotationViewDragStateNone];
     }
     if (myState == MKAnnotationViewDragStateDragging) {
          NSLog(@"dragging");
     }
     if (myState == MKAnnotationViewDragStateCanceling) {
          NSLog(@"cancel");
     }
     if (myState == MKAnnotationViewDragStateNone) {
          NSLog(@"none");
     }
}

一切正常,注释向上移动,可拖动,当我发布注释时,mapview会收到“dragstateending”。

但是现在我希望动画在一段时间内运行并将dragStateStarting更改为以下内容:

if (myState == MKAnnotationViewDragStateStarting) {
          NSLog(@"starting");
          CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
          [UIView animateWithDuration:1.0
           animations:^{ self.center = endPoint; }
           completion:^(BOOL finished){ [self movePinUpFinished]; }];
     }

动画在一秒钟内按需运行,注释可拖动。但是当我发布注释时,mapview没有通过委托接收结尾。我还认识到,当我使用“UIView animateWithDuration ...”进行动画时,在开始拖动之后,当动画开始时,注释的气球就会打开。当我在没有动画的情况下设置新中心时,气球会保持关闭状态,只有在通过释放注释完成拖动后才会打开。

我做错了什么?这是覆盖setDragState的正确方法吗?我真的要打电话给超级班吗?但是如果没有在超类中设置拖尾状态,我的mapview就没有意识到拖尾状态的变化。

我想知道MKPinAnnotationView的原始实现,但因为它是一个内部类,我找不到setDragState方法的描述。

请求帮助。欢呼声,

3 个答案:

答案 0 :(得分:23)

我有pin拖动工作但是试图找出为什么当你不覆盖setDragState时发生的引脚annimations - 不再适用于我的实现。你的问题包含我的回答..谢谢!

你的代码的一部分问题是,一旦你覆盖setDragState函数,根据xcode文档,你负责根据新的状态更新dragState变量。我也有点担心你的代码自己调用(setDragState调用[self setDragState])。

这是我最终得到的代码(在你的帮助下),它可以完成我所期望的所有升降机,拖动和跌落。希望这对你也有帮助!

- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    if (newDragState == MKAnnotationViewDragStateStarting)
    {
        // lift the pin and set the state to dragging

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
                             { self.dragState = MKAnnotationViewDragStateDragging; }];
    }
    else if (newDragState == MKAnnotationViewDragStateEnding)
    {
        // save the new location, drop the pin, and set state to none

        /* my app specific code to save the new position
        objectObservations[ACTIVE].latitude = pinAnnotation.coordinate.latitude;
        objectObservations[ACTIVE].longitude = pinAnnotation.coordinate.longitude;
        posChanged = TRUE;
        */

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
                             { self.dragState = MKAnnotationViewDragStateNone; }];
    }
    else if (newDragState == MKAnnotationViewDragStateCanceling)
    {
        // drop the pin and set the state to none

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

答案 1 :(得分:3)

虽然Brian's solution有效,但它没有考虑用户手指阻止正在操纵的注释视图。

这意味着用户拖动后无法精确放置引脚。标准MKPinAnnotationView在这方面做得很好,当手指开始拖动时,针被抬起到手指上方,并且针的视点用于放置而不是之前居住的中心点在手指下。

除此之外,我的实现还在拖动后放下引脚时添加了另一个动画,方法是抬起引脚并以更高的速度放下引脚。这与原生用户体验非常接近,并且会受到用户的欢迎。

请查看我的gist on GitHub for the code

设置委托是可选的真的很酷,当注释视图被放回到地图上时,可选择发送通知。

答案 2 :(得分:2)

我没有多少研究Ben的代码,但它对我没用。所以我试过Brian的,它的效果非常好。非常感谢!我一直试图在拖拉期间解决注释的动画很长一段时间。

但我对Brian的解决方案有一个建议。我认为最好支持MKMapKit的委托,并通知它更改dragState并在标准委托的方法中保存新位置:- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState。这是我的代码:

DraggableAnnotationView.h:

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface DraggableAnnotationView : MKAnnotationView 
{
    id <MKMapViewDelegate> delegate;
    MKAnnotationViewDragState dragState;
}

@property (nonatomic, assign) id <MKMapViewDelegate> delegate;
@property (nonatomic, assign) MKAnnotationViewDragState dragState;
@property (nonatomic, assign) MKMapView *mapView;

@end

DraggableAnnotationView.m:

#import "DraggableAnnotationView.h"
#import "MapAnnotation.h"

@implementation DraggableAnnotationView
@synthesize delegate, dragState, mapView;



- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
    [delegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState];

    if (newDragState == MKAnnotationViewDragStateStarting) {
        // lift the pin and set the state to dragging
        CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
         { dragState = MKAnnotationViewDragStateDragging; }];
    } else if (newDragState == MKAnnotationViewDragStateEnding) {
        // drop the pin, and set state to none

        CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
        [UIView animateWithDuration:0.2
                         animations:^{ self.center = endPoint; }
                         completion:^(BOOL finished)
         { dragState = MKAnnotationViewDragStateNone; }];
    } else if (newDragState == MKAnnotationViewDragStateCanceling) {
        // drop the pin and set the state to none

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

- (void)dealloc 
{
    [super dealloc];
}

@end