MKMapView moving Annotations Automatically - animate them?

Lee Armstrong picture Lee Armstrong · Nov 6, 2010 · Viewed 22.2k times · Source

I have a data set of annotations that can update very quickly. At the moment I remove all annotations before replotting them back onto the map.

NSArray *existingpoints = [mapView.annotations filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"!(self isKindOfClass: %@)", [MKUserLocation class]]];
[mapView removeAnnotations:existingpoints];

I calculate where they are anyway within the custom object so would like to be able to call this and "move" the annotation without removing and re-adding it back to the map. The example call I make which works and I would like to almost "poll" is below.

- (CLLocationCoordinate2D) coordinate
{
    CLLocationCoordinate2D coord;
    coord.latitude = [lat doubleValue];
    coord.longitude = [lon doubleValue];


        double differencetime = exampleTime;
        double speedmoving;
        double distanceTravelled = speedmoving * differencetime;

        CLLocationDistance movedDistance = distanceTravelled;
        double radiansHeaded = DEG2RAD([self.heading doubleValue]);
        CLLocation *newLocation = [passedLocation newLoc:movedDistance along:radiansHeaded];
        coord = newLocation.coordinate;

    return coord;
}

As requested, the .h file of the Object, I do not have a SetCoordinate method..

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

@interface TestObject : NSObject <MKAnnotation>{
    NSString *adshex;
    NSString *lat;
    NSString *lon;


    NSString *title;
    NSString *subtitle;


    CLLocationCoordinate2D coordinate;
}
@property(nonatomic,retain)NSString *adshex;
@property(nonatomic,retain)NSString *lat;
@property(nonatomic,retain)NSString *lon;


@property(nonatomic,retain)NSString *title;
@property(nonatomic,retain)NSString *subtitle;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;


- (CLLocationCoordinate2D) coordinate;

@end

Answer

user467105 picture user467105 · Nov 6, 2010

If you update the annotation's coordinate using a setCoordinate method (or equivalent), the map view will automatically update the position of the annotation on the view. This page in the docs says the following:

Important: When you implement the coordinate property in your class, it is recommended that you synthesize its creation. If you choose to implement the methods for this property yourself, or if you manually modify the variable underlying that property in other parts of your class after the annotation has been added to the map, be sure to send out key-value observing (KVO) notifications when you do. Map Kit uses KVO notifications to detect changes to the coordinate, title, and subtitle properties of your annotations and make any needed changes to the map display. If you do not send out KVO notifications, the position of your annotations may not be updated properly on the map.

The map view will only know to re-read the coordinate property of the annotation if it's told (via KVO) that the coordinate has changed. One way to do that is implement a setCoordinate method and call that wherever you have the code that updates the annotation's location.

In your code, you are re-calculating the coordinate in the readonly coordinate property itself. What you could do is add this to the annotation .m file (and to the .h):

- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
{
    //do nothing
}

and in the place where you update locations, call the setCoordinate method on the annotation:

[someAnnotation setCoordinate:someAnnotation.coordinate];

You could do this in the place where you currently remove/re-add the annotations.

The above call looks funny because you have the coordinate re-calc in the coordinate-getter method. Although it should work as a quick fix/test, I don't recommend using it regularly.

Instead, you could re-calc the annotation's location outside (where you currently remove/re-add the annotations) and pass the new coordinate to setCoordinate. Your annotation object could store its new location in the lat/lng ivars you currently have (set them in the setCoordinate and use only those to construct a CLLocationCoordinate2D to return from the getter) or (better) use the coordinate ivar itself (set it in setCoordinate and return it in the getter).