How to move a MKAnnotation without adding/removing it from the map?
Asked Answered
L

11

39

Is it possible to move the coordinate of a MKAnnotation without adding and removing the annotation from the map?

Lingonberry answered 13/2, 2010 at 1:44 Comment(0)
A
24

In Swift 4.0 and IOS 11.0, I just add dynamic attribute to the coordinate property of child class of MKAnnotation class and it works: All Annotations on MapView update their location if coordinate value of MKAnnotation objects are updated:

class CarAnnotation: NSObject, MKAnnotation {
    @objc dynamic var coordinate: CLLocationCoordinate2D
    //Add your custom code here
}
Albacore answered 15/3, 2018 at 9:30 Comment(2)
Should be the accepted answer. I never knew about this! You just saved me a bunch of time. ;)Giro
This is golden <3Holifield
G
19

I found a pretty simple method. I wanted to update my player's position smoothly without removing and then re-adding the player annotation. This works well for pre-iOS4 apps, and what it does is both move the pin and center the map on the pin's new location:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[player setCoordinate:aLoc.coordinate];
[mapView setCenterCoordinate:aLoc.coordinate animated:NO];
[UIView commitAnimations];

Here's the same thing but using the recommended block feature for iOS4:

[UIView animateWithDuration:0.5 animations:^{
    [player setCoordinate:aLoc.coordinate];
    [mapView setCenterCoordinate:aLoc.coordinate animated:NO];
}];
Gloss answered 5/7, 2010 at 13:47 Comment(3)
Any method that's updating your pin's position. For me, I have another object sending my map view controller a new position, which I then use to update my player pin's position. But you could receive a new position from an external source, or by the user touching a button or using drag-and-drop, whatever.Gloss
dunno why, but this method makes MapView to redraw itself, so I personally didn't like it.Sezen
This seems to work. I'm just moving the annotation, not resetting the center - but I don't get any MapView redraws. One issue I do get (which is also an issue with the other, more complex, solutions here) is that the annotation vanishes if you zoom during the animation phase. It reappears at the end of the animation, so it's not a huge deal.Whitted
R
7

It is indeed possible without removing and adding the annotation off-course.

You'll need to observe your MKAnnotation's coordinate and update the MKAnnotationView's position when it changes. Alternatively you can (and this is probably the more elegant way of doing it) derive your own MKAnnotation and MKAnnotationView and make them "movable".

See Moving-MKAnnotationView for a detailed example which also animates the position of the annotation.

Reasoned answered 14/3, 2011 at 15:19 Comment(0)
F
4

Just use KVO:

[annotation willChangeValueForKey:@"coordinate"];
[annotation didChangeValueForKey:@"coordinate"];

If your MKAnnotation has an setCoordinate method, just include these lines right there:

- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
    [self willChangeValueForKey:@"coordinate"];
    coordinate = newCoordinate;
    [self didChangeValueForKey:@"coordinate"];
}
Felicific answered 13/3, 2012 at 18:30 Comment(1)
Or if you extend MKPointAnnotation then it already has a coordinate property that supports KVO and automatically moves on the map.Leucas
L
4

Subclass MKPointAnnotation and it has the capability to update the annotation view on the map to the new coordinate automatically.

Leucas answered 26/4, 2014 at 19:17 Comment(2)
Thanks its work smoothly. just update coordinate of annotation.Compressive
In Swift, be sure to use dynamic var coordinate or else KVO will not be automaticUrquhart
S
2

Don't know whether it's more generally possible, but it is if you have your own custom MKAnnotationView.

I was able to adapt the approach documented at http://spitzkoff.com/craig/?p=108 by Craig Spitzkoff:

  1. Give your custom MKAnnotationView a reference to your MKAnnotation object
  2. Add an updateCoordinates method to the MKAnnotation object and call it when you want to change location of the annotation
  3. Call regionChanged on the custom MKAnnotationView to let it know when to reposition itself (e.g. when MKAnnotation has updated coordinates)
  4. In drawRect on the internal view object owned by your custom MKAnnotationView you can reposition using the coordinates of the MKAnnotation (you're holding a reference to it).

You could likely simplify this approach further - you may not require an internal view object in all circumstances.

Swanner answered 15/2, 2010 at 3:45 Comment(2)
Yeah that's the solution I came up with, seems weird to have drawRect determine it's own frame though.Lingonberry
Are you able to provide a sample to this at all please?Ethnogeny
H
1

If you keep a reference to your annotation, make a change to the coordinate property, then add the annotation to the map again, the location of the annotation view will update. You do not need to remove the annotation (which will cause the annotation view to momentarily disappear). This will not give you a nice transition for when the annotation coordinate is updated however. It also does not result in a memory leak nor are there a multiple of the same annotation added to the map (if you look at the number of annotations on the mapview, it remains constant when you re-add the annotation).

Haematoma answered 16/2, 2011 at 13:58 Comment(1)
This actually does not work, if I remove and add the annotation it works. But simply modifying the coordinates properties and using mapView.addAnnotation(myAnnotation) doesn't move the annotation, but if I print the coordinates, they effectively change. I wonder why.Jezebel
S
1

Most answers are based on fact that your Annotation has been made via MKPlacemark extending. But what about any other object (i.e. custom image for marker) which only implements MKAnnotation protocol? It wouldn't have the setCoordinate method. So here is the answer based on code, provided by 100grams

    MKAnnotationView *av = [self.mapView viewForAnnotation:user];
    //user is an object which implements <MKAnnotation>
    // You will probably get your MKAnnotationView in your own way
    if (av){
        // here user returns CLLocationCoordinate2D coordinate  
        MKMapPoint mapPoint = MKMapPointForCoordinate([user getLastLocation]); 
        // converting mapPoint to CGPoint 
        CGPoint newPos;
        CGFloat zoomFactor =  self.mapView.visibleMapRect.size.width / self.mapView.bounds.size.width;
        newPos.x = mapPoint.x/zoomFactor;
        newPos.y = mapPoint.y/zoomFactor;
        // animation for annotation's move
        static NSString *POSITION_KEY=@"positionAnimation";
    static NSString *PATH_ANIMATION_KEY=@"position";
    if (MKMapRectContainsPoint(self.mapView.visibleMapRect, mapPoint)) {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:PATH_ANIMATION_KEY];
        animation.fromValue = [NSValue valueWithCGPoint:av.center];
        animation.toValue = [NSValue valueWithCGPoint:newPos];  
        animation.duration = 0.3;
        animation.delegate = av;
        animation.fillMode = kCAFillModeForwards;
        [av.layer addAnimation:animation forKey:POSITION_KEY];
    }
        // moving view
        av.center = newPos;

so You basically need to get the corresponding MKAnnotationView which could be also achieved by iteration via all of annotations and it's new location. I made an array holder for all of it (NSMutableArray *mapAnnotaions) so now I could iterate just like:

for (id annotation in mapAnnotaions) {call refresh method here for id}
Sezen answered 18/7, 2012 at 16:31 Comment(4)
:- I clearly understand what u have mentioned in the above code snippet. I have upvoted your answer. Can you please explain on how to stop the animation at particular map points for a certain 2 seconds and then start moving the object again?Tanyatanzania
About pause: I can't provide you the code but I guess you could use [NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:] for pause between map points. That also will give user a chance to use GUI while animation is paused.Sezen
:- The above approach might work if we need to stop the animation after certain time interval. But here we need to stop the animation based on the coordinate value and then resume after certain seconds. We can use the timer in this case to resume the animation but how to stop the animation based on the coordiinate value?Tanyatanzania
https://mcmap.net/q/109303/-cancel-a-uiview-animation maybe? or #1153418Sezen
I
1

After you update the coordinates or some other properties of the annotation, just call following MKMapViewDelegate method to redraw your annotation:

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

e.g.:

myAnnotation.coordinate = CLLocationCoordinate2DMake([newLat doubleValue], [newLon doubleValue]);
[self mapView:self.mapView viewForAnnotation:myAnnotation];
Impede answered 3/7, 2014 at 23:51 Comment(0)
A
0

Take a look at my MapKitDragAndDrop sample, it allows you to update MKAnnotation and move MKAnnotationView on both iPhone OS 3 and iOS 4 (will use built-in dragging support).

Acetous answered 30/6, 2010 at 22:41 Comment(0)
C
-6

It doesn't seem possible to change the coordinate of a MKAnnotation object and then inform the MKMapView of the change.

However, removing the previous annotation from the map, changing the coordinate of your annotation object and adding it back to the map works well.

[theMapView removeAnnotation:myAnnotation]; 
[myAnnotation setLatitude:newLatitude];
[theMapView addAnnotation:myAnnotation];

Why don't you want to do it this way?

Concordat answered 13/2, 2010 at 11:53 Comment(2)
the OP is asking for this, and for many users its ncier to see a smooth transitionAnacardiaceous
It is possible, see my answer above.Leucas

© 2022 - 2024 — McMap. All rights reserved.