Search code examples
iphoneiosanimationmkmapviewios6

Controlling the animation speed of MKMapView in iOS6


I'm trying to follow a car on a map view.

This code should animate the car and the map with the same speed, so that the annotation view always appears in the center:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration: 1.0];
[UIView setAnimationBeginsFromCurrentState:YES];

[car setCoordinate:coord];
[mapView setCenterCoordinate:coord];

[UIView commitAnimations];

It worked fine in iOS 5. In iOS 6 the map is not animating anymore but the car does animate.

I tried [mapView setCenterCoordinate:co animated:YES], but then I cannot control the animation speed. It will always animate with the default duration (0.2s).


Solution

  • It seems it is not possible to control the animation speed in iOS 6 when using these methods in MKMapView:

    - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;
    - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
    - (void)setVisibleMapRect:(MKMapRect)mapRect animated:(BOOL)animate;
    - (void)setVisibleMapRect:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets animated:(BOOL)animate;
    

    However I found a private method that has a duration parameter. So I solved my issue by subclassing MKMapView and overriding the private method (which only exists in iOS6):

    MyMapView.h

    #import <MapKit/MapKit.h>
    
    @interface MyMapView : MKMapView
    
    @property(nonatomic) BOOL overridesAnimationDuration;
    @property(nonatomic) NSTimeInterval mapAnimationDuration;
    
    @end
    

    MyMapView.m

    @interface MKMapView (Private)
    - (void)_setZoomScale:(float)scale centerMapPoint:(CLLocationCoordinate2D)center duration:(double)d animationType:(int)animType;
    @end
    
    @implementation MyMapView
    - (void)_setZoomScale:(float)scale centerMapPoint:(CLLocationCoordinate2D)center duration:(double)d animationType:(int)animType
    {
        if (_overridesAnimationDuration) {
            d = _mapAnimationDuration;
        }
        [super _setZoomScale:scale centerMapPoint:center duration:d animationType:animType];
    }    
    @end
    

    I've added 2 properties that allow you to override the default animation time. To use it set overridesAnimationDuration to YES before a region change, set mapAnimationDuration to the desired duration and switch overridesAnimationDuration to NO in the delegate call mapView:regionWillChangeAnimated:.

    Note, this might not work in future iOS versions because it is private API. Apple can remove or change this method.