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).
I ran into the same kind of issue today. I think the problem does not rely on MKMapView, but the (new) way ios6 manages animations.
It seems that in ios6, if an animation occurs before a previous one has finished (depending on the run loop), the older gets interrupted by the new one. I think this only occurs if the "beginsFromCurrentState" option or property (depending if you are using block based animation or not) is used (by the new one).
To be sure, I think you should try to use block-based animation to see if your animation is really interrupted by another one.
This code must be equivalent to yours, and allows you to see if your animation has been interrupted or cancelled (if "finished" is false) :
[UIView animateWithDuration:1.0 delay:0.0f options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState) animations:^{
[car setCoordinate:coord];
[mapView setCenterCoordinate:coord];
} completion:^(BOOL finished){
NSLog(@"has not been interrupted : %d", finished);
}];
(in iOS < 6,"finished" should be true...)
In my case, it appeared that my animation was interrupted by the following UIViewController's methods, which was performed by the system in an animation block, interrupting my animation chain :
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
I found that the standard UIView AnimationWithDuration would apply if formatted like so:
// create a region with a center coordinate and a degree span
let center = CLLocationCoordinate2D(latitude: 42.3601, longitude: -71.0689)
let span = MKCoordinateSpanMake(1.0, 1.0)
let region = MKCoordinateRegion(center: center, span: span)
UIView.animateWithDuration(3.0,
delay: 0.0,
options: .CurveEaseInOut | .AllowUserInteraction,
animations: {
self.mapView.setRegion(region, animated: true);
},
completion: { finished in
println("completed 3 second animation to new region")
})
Hope that works for you too! :)
(note: It was pointed out to me, that this response was geared towards iOS7/8 and that the question was for iOS6. )
And, this simple will actually work fine...
let c = mkMap.userLocation.coordinate
let r = CLLocationDistance( 1500 )
let cr = MKCoordinateRegionMakeWithDistance( c, r, r )
UIView.animate(withDuration: 1.4, animations: { [weak self] in
self?.mkMap.setRegion( cr, animated: true)
})
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.