Pregunta

I’ve read a lot about MKAnnotation and how you need to implement setCoordinate in your subclass as well as draggable=TRUE in order to make the whole shebang draggable.

My situation is that in my iOS7-only app, my annotation is draggable no matter whether I implement setCoordinate or not…but the problem is that I need to tap it first (which pops out the callout accessory) AND THEN long tap it, and only then will it hover in the air above the map and can be dragged. This is confusing for the user because it’s different to how it is in the standard Maps app. Notice in the Maps app that a long tap on an annotation will make it hover & draggable without a prerequisite tap.

I’ve tried implementing setCoordinate, but this doesn’t make any difference. Other than that my annotation subclass just stores the latitude & longitude, which works fine. I just want it to be draggable straight away on the long tap.

Relevant code for my View Controller which implements MKMapViewDelegate. I can verify this by putting breakpoints in delegate methods.

- (void)viewDidLoad
{
        [super viewDidLoad];
[mapView setDelegate:self];    
}

-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:
(id <MKAnnotation>)annotation {

    MKPinAnnotationView *pinView = nil;
    if(annotation != mapView.userLocation)
    {
        static NSString *defaultPinID = @"pointPin";
        pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
        if ( pinView == nil ) {
            pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
        }
        pinView.pinColor = MKPinAnnotationColorRed;
        if ([annotation isKindOfClass:[SimpleMapAnnotation class]]) {
            SimpleMapAnnotation *simpleMapAnnotation = (SimpleMapAnnotation*)annotation;
            if ([simpleMapAnnotation color]) {
                pinView.pinColor = [simpleMapAnnotation color];
            }
            if (simpleMapAnnotation.moveable) {
                pinView.draggable=TRUE;
                // delete button to remove an annotation
                UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
                [button setImage:[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"trash" ofType:@"png"]] forState:UIControlStateNormal] ;
                button.frame = CGRectMake(0, 0, 23, 23);
                pinView.rightCalloutAccessoryView = button;
            }
        }
        pinView.canShowCallout = YES;
        pinView.animatesDrop = YES;
    }
    else {
        [mapView.userLocation setTitle:@"I am here"];
    }
    return pinView;
}

- (void)mapView:(MKMapView *)theMapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control{
    if([view.annotation isKindOfClass:[SimpleMapAnnotation class]]){
        SimpleMapAnnotation *annotation = (SimpleMapAnnotation*)view.annotation;

        //remove the point from the database
    //<snip>

        [UIView animateWithDuration:0.3 delay:0.0 options:0 animations:(void (^)(void)) ^{
            //remove the annotation from the map
            view.alpha = 0.0f;
        }
                         completion:^(BOOL finished){
                             [theMapView removeAnnotation:annotation];
                             view.alpha=1.0f;
                         }];

    }
}


- (void)mapView:(MKMapView *)mapView
 annotationView:(MKAnnotationView *)annotationView
didChangeDragState:(MKAnnotationViewDragState)newState
   fromOldState:(MKAnnotationViewDragState)oldState
{
    if (newState == MKAnnotationViewDragStateEnding)
    {
        if ([annotationView.annotation isMemberOfClass:[SimpleMapAnnotation class]]) {
            SimpleMapAnnotation *simpleMapAnnotation = (SimpleMapAnnotation*)annotationView.annotation;
            simpleMapAnnotation.latitude = [NSNumber numberWithDouble:simpleMapAnnotation.coordinate.latitude];
            simpleMapAnnotation.longitude = [NSNumber numberWithDouble:simpleMapAnnotation.coordinate.longitude];
        }
        CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
        NSLog(@"dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
    }
}
¿Fue útil?

Solución 2

I ended up using this solution, which not only gets rid of the need for the first tap before long-tap-drag, but also is very smooth feeling for the user when dragging and has a large draggable area. There's a bit of an explanation from Azavea who implemented it, on their blog here.

I'm nearly done (famous last words) on the next version of my app which uses a slightly modified version of the above, namely that it (a) drags as in the original, (b) allows a tappable action on the annotation and (c) specifically ignores a long-tap on the annotation so that when attempting to drag you don't get annoyed by the unwanted tap action. Once I am done, I plan to fork or branch (I'm a bit new to what you do in this situation) the above github project to add that functionality. If I get really lucky I'd also like to be able to fix or at least configure the quick swipe issue they mention in the 2nd last paragraph of that blog I linked to.

Otros consejos

To begin the drag of the MKAnnotationView object it should be selected first. It's by design.

If you want to start moving of the annotation view immediately on a long tap you should set the selected property to YES before the touches of that long tap have been delivered to object.

To do this make a successor of MKPinAnnotationView class as following:

// MKImmideateDragPinAnnotationView.h
@interface MKImmideateDragPinAnnotationView : MKPinAnnotationView
@end


// MKImmideateDragPinAnnotationView.m
@implementation MKImmideateDragPinAnnotationView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self setSelected:YES];
    [super touchesBegan:touches withEvent:event];
}

@end

Then change MKPinAnnotationView class to MKImmideateDragPinAnnotationView class at pinView allocation in your code:

-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation {
    ...

    if ( pinView == nil ) {
        pinView = [[MKImmideateDragPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
    }

    ...

}

Your pin will start to drag immediately on long tap. And will show callout on single tap, as usual.

That trick will work with any MKAnnotationView class in iOS 6.xx - iOS 7.xx.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top