How to make custom MKAnnotation draggable

ios, mkannotation, mkmapview, objective-c

Solution

As already mentioned, for an annotation to be draggable, it must implement a `setCoordinate:` method.

Additionally, since iOS 7, you may also need to implement the `mapView:annotationView:didChangeDragState:` method (see Draggable Pin does not have a fixed position on map and iOS MapKit dragged annotations (MKAnnotationView) no longer pan with map).

You can either implement the `setCoordinate:` method explicitly yourself or just declare a writeable `coordinate` property (named exactly like that) and synthesize it (and the getter and setter methods will be automatically implemented for you).

(Note that if you use the pre-defined `MKPointAnnotation` class, you don't need to do this because that class already implements a settable `coordinate` property.)

In your `NavigationAnnotation` class, to implement the explicit, manual solution to work with the existing `theCoordinate` property, just add the `setCoordinate:` method to your class implementation (keep the existing getter method):

-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
{
    self.theCoordinate = newCoordinate;
}

You may also need to implement the `didChangeDragState:` method in the class that implements the map view delegate (the same one that has the `viewForAnnotation` method) otherwise after dragging, the annotation view will hover in-place above the map even while it is panned or zoomed underneath. An example implementation of the method as given by Chris K. in his answer:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState
{
    if (newState == MKAnnotationViewDragStateStarting)
    {
        annotationView.dragState = MKAnnotationViewDragStateDragging;
    }
    else if (newState == MKAnnotationViewDragStateEnding || newState == MKAnnotationViewDragStateCanceling)
    {
        annotationView.dragState = MKAnnotationViewDragStateNone;
    }
}

Problem

I need MKAnnotation with different pin image. So I created following: ``` @interface NavigationAnnotation : NSObject <MKAnnotation> - (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate; ... @interface NavigationAnnotation () @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *address; @property (nonatomic, assign) CLLocationCoordinate2D theCoordinate; @end @implementation NavigationAnnotation - (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate { if ((self = [super init])) { if ([name isKindOfClass:[NSString class]]) { self.name = name; } else { self.name = @"Unknown charge"; } self.address = address; self.theCoordinate = coordinate; } return self; } - (NSString *)title { return _name; } - (NSString *)subtitle { return _address; } - (CLLocationCoordinate2D)coordinate { return _theCoordinate; } ``` And add it like this: ``` - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{ static NSString *identifier_OrderAnnotation = @"NavigateAnnotation"; if ([annotation isKindOfClass:[MKUserLocation class]]) return nil; if ([annotation isKindOfClass:[NavigationAnnotation class]]) { MKAnnotationView *annotationView = (MKAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier_OrderAnnotation]; if (annotationView == nil) { annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier_OrderAnnotation]; annotationView.enabled = YES; annotationView.canShowCallout = YES; annotationView.draggable = YES; annotationView.image = [UIImage imageNamed:@"myimage.png"]; } else { annotationView.annotation = annotation; } return annotationView; } } ``` Annotation shows on map fine but it is not draggable from some reason :(

Original source

Related problems