How to slow MKMapCamera movement? - ios

I am coding in iOS.
I have an NSArray, which contains a few MKMapCameras. I want to display MKMapCameras from the array one after another.
I put a while loop and used [self.mapView setCamera:nextCamera animated:YES];
However, this is only showing the first and the last views. Everything in between is going too fast.
I want to slow down the movement of each camera. Is there a way to achieve it using CATransaction or using any other animation tricks. If so, could you please show me an example code?
Want to give an update... I tried below code. But it isn't working... Camera movements are fast as I mentioned earlier.
[CATransaction begin];
[CATransaction setAnimationDuration:5.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[CATransaction setCompletionBlock:^{
[self.mapView setCamera:nextCamera animated:YES];
}];
[CATransaction commit];

After fiddling with it a few hours, I figured out a way to make it work. Thought of sharing the same with everyone...
I made two changes. I replaced CATransaction with UIView's animation. I also removed Camera's default animation settings, which was conflicting with UIView's animation.
Below is the code.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:2.5];
self.mapView.camera = nextCamera;
[UIView commitAnimations];

According to the WWDC 'Putting MapKit in Perspective' video you should avoid any approach using completion handlers for animating map cameras in sequence. Rather you should set a delegate on your map view and listen for the regionDidChangeAnimated: call to trigger the next camera in your sequence. This way the speed of the camera movement can be controlled with animateWithDuration:
-(void)flyToLocation:(CLLocationCoordinate2D)toLocation {
CLLocationCoordinate2D startCoord = self.mapView.camera.centerCoordinate;
CLLocationCoordinate2D eye = CLLocationCoordinate2DMake(toLocation.latitude, toLocation.longitude);
MKMapCamera *upCam = [MKMapCamera cameraLookingAtCenterCoordinate:startCoord
fromEyeCoordinate:startCoord
eyeAltitude:80000];
MKMapCamera *turnCam = [MKMapCamera cameraLookingAtCenterCoordinate:toLocation
fromEyeCoordinate:startCoord
eyeAltitude:80000];
MKMapCamera *inCam = [MKMapCamera cameraLookingAtCenterCoordinate:toLocation
fromEyeCoordinate:eye
eyeAltitude:26000];
self.camerasArray = [NSMutableArray arrayWithObjects:upCam, turnCam, inCam, nil];
[self gotoNextCamera];
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
[self gotoNextCamera];
}
-(void)gotoNextCamera {
if (self.camerasArray.count == 0) {
return;
}
MKMapCamera *nextCam = [self.camerasArray firstObject];
[self.camerasArray removeObjectAtIndex:0];
[UIView animateWithDuration:3.0 animations:^{
self.mapView.camera = nextCam;
}];
}

Related

iOS 10 MapKit previous layer zoom issue

I am working in a map application which renders a polyline over a map. I am having an issue when zooming, it keeps the previous polyline on the tile.
I have tried to force redrawing:
[self.mapView reloadInputViews];
[self.mapView.layer setNeedsDisplay];
[self.mapView setNeedsDisplay];
Also I tried to slow the zoom speed, but the issue is still in there:
[MKMapView animateWithDuration:2
delay:0
usingSpringWithDamping:0.6
initialSpringVelocity:10
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self.mapView setVisibleMapRect:unionRectThatFits
edgePadding:UIEdgeInsetsMake(20, 10, 20, 10)
animated:YES];
}
completion: nil];
Does anyone know about this?
I found the resolution to the problem here: How to refresh an MKOverlayRenderer when mapView change
So I added:
override var boundingMapRect: MKMapRect {
return MKMapRectWorld
}
To my MKPolyline & MKCircle subclass.

UIView animateWithDuration how to Cancel by touch event(No interaction with MapView)

I have a problem with my function.
[UIView animateWithDuration:5 animations:^{
//set end coordinates for marker = MKAnnotationPoint
[self.followDriverMarker setCoordinate:CLLocationCoordinate2DMake(latitde, longitude)];
//set end coordinate for camera/map
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(latitde, longitude) animated:NO];
}
completion:^(BOOL finished) {
if (finished) {
if (self.currentPosition < [self.followDriverList count] - 2) {
self.currentPosition++;
//start next one
[self runAnimation];
} else {
//animation is finished
//TODO
self.isAnimationRunning = NO;
}
}
}];
The function will look in a List if there are locations left. If so it will run again. That works. The only problem is. If the animation is running. There is no interaction possible with the mapView. The other problem is that i cannot find how to cancel,stop or remove my Animation.
If I put:
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(latitde, longitude) animated:NO];
inside the the completionBlock, I have interaction with map. But I don't want to do that because I want to animate it the same time. Also here I can't find a way to cancel the animation.
Please don't say removeAllAnimation. This is not working.
I believe this answers your question.
Basically you commit a new animation on the same target with a short duration. By setting the setAnimationBeginsFromCurrentState flag you prevent weird jumps.

Stop specific UIViewAnimation block - iOS

I have an iOS application which runs a few different UIViewAnimation blocks to animate a few different objects on screen. This all works, but how can I stop ONE of the animation blocks WITHOUT stopping the rest?
I have tried using the following method:
[button.layer removeAllAnimations];
But it doesn't do anything, the animation just continues.
I then tried to use a simple BOOL value and get the animation to return from the method once the BOOL is set to "NO", but that doesn't work either.
Here is the animation which I am trying to stop:
-(void)undo_animation:(int)num {
// Fade out/in the number button label
// animation which lets the user know
// they can undo this particular action.
[UIView animateWithDuration:0.5 delay:0.2 options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction) animations:^{
// Mostly fade out the number button label.
((UIButton *)_buttons[num]).alpha = 0.2;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 delay:0.2 options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction) animations:^{
// Fade in the number button label.
((UIButton *)_buttons[num]).alpha = 1.0;
} completion:^(BOOL finished) {
// Stop the animation if the BOOL
// is set to 'NO' animations.
if (anim_state_button == NO) {
return;
}
}];
}];
}
Thanks, Dan.
You can't reach running animation property if you're using UIKit Animations. So I suggest to using Core Animation if you want to modify animation in the runtime of it.
And it's too simple to removing alpha of the view like below.
CABasicAnimation* fadein= [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadein setToValue:[NSNumber numberWithFloat:1.0]];
[fadein setDuration:0.5];
[[moviepic layer]addAnimation:fadein forKey:#"MyAnimation"];
after adding animation to layer animation will start and than you can use delegate methods to be informed about animationDidFinish: method
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
NSLog(#"Animation interrupted: %#", (!flag)?#"Yes" : #"No");
}
Also you can reach whenever you want from using;
[[moviepic layer] animationForKey:#"MyAnimation"];
And of course you need to add CoreAnimation Framework to your project.
Hope it helps.
I think the simple way is that remove all animations from your View Layer as all the animation are by default added into the View's Layer.
[yourRequiredView.layer removeAllAnimations];
My understanding is that removing all animations from the relevant layer should stop all animations. How about [((UIButton *)_buttons[num]).layer removeAllAnimations]; ?

Fake Pan Gesture in Code

I'm trying to fake pan gesture, but am not sure how to do it. There is some complicated code determining what happens with the pan gesture, so before going in and abstracting out all the logic, I was wondering if anybody knew of a way to fake the gesture. Thanks.
Unfortunately the API does not provide a way to trigger the gesture programmatically nor does it provide public access to the targets and selectors of the UIGestureRecognizer.
However, if you are wanting to do this for unit tests, you could get access to the targets and selectors by doing the following:
NSArray *targets = [panGesture valueForKey:#"_targets"];
NSArray *actions = [panGesture valueForKey:#"_actions"];
You can then loop through those arrays and call the action on the target:
[target performSelector:selector withObject:panGesture];
To "fake a pan gesture" I assume you mean move the view somewhere it currently isn't. UIView animations can be used for this.
[UIView beginAnimations:#"Fake-A-Pan" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:0.5f];
self.frame = CGRectMake(/* new coordinates for frame here */);
[UIView commitAnimations];
By using an animation to change the location of the view, you can essentially "fake a pan".
Edit As a comment suggested, Block-Based animations can also be used.
[UIView animateWithDuration:.5f
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
self.frame ... // adjust your view here
}
completion:^(BOOL finished) {
NSLog(#"Done animating");
}];

Controlling Animation Duration in Google Maps for iOS

The documentation for Google Maps for iOS states that:
Call one of several methods that allow you to animate the camera moving to a new location. You can control the duration of the animation with CoreAnimation.
For the life of me, I can't figure out how to control the animation duration. I have tried using UIView animations, like:
[UIView animateWithDuration: 5 animations:^{
GMSCameraPosition *camera = [self newCamera];
self.mapView.camera = camera;
} completion:^(BOOL finished) {
}];
And I have looked at CALayer animations in CoreAnimation. However, I don't know how you would apply a layer animation to the map view.
Can someone point me in the right direction please?
I found the answer ... you can control the animation duration by wrapping one of the animate* methods in a CATransaction, like this:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat: 1.0f] forKey:kCATransactionAnimationDuration];
// change the camera, set the zoom, whatever. Just make sure to call the animate* method.
[self.mapView animateToCameraPosition: [self newCamera]];
[CATransaction commit];
for Swift 3.0:
CATransaction.begin()
CATransaction.setValue(1.5, forKey: kCATransactionAnimationDuration)
// your camera code goes here, example:
// mapView.animate(with: update)
CATransaction.commit()
The bigger the value (1.5 in this case), the slower the animation.
Swift 2.0
CATransaction.begin()
CATransaction.setValue(NSNumber(float: 1.0), forKey: kCATransactionAnimationDuration)
// change the camera, set the zoom, whatever. Just make sure to call the animate* method.
CATransaction.commit()
what a pitty that using the same methods you provided there is no way to know if the animation has ended.
Yes I know, there is a CATransaction completion block using this method but it does simply not work! :(
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat: 1.0f] forKey:kCATransactionAnimationDuration];
[CATransaction setCompletionBlock:^{
// ... whatever you want to do when the animation is complete
}];
[self.googleMapsView animateToCameraPosition:[GMSCameraPosition
cameraWithLatitude:LATITUDE
longitude:LONGITUDE
zoom:ZOOM]];
[CATransaction commit];
And I can't use MapView:didIdle hack to know that the animation has ended because it will not be called if there is no camera position change.
Anyone knows how to detect animateon has ended event?
FOUND A THREAD ABOUT THIS (solved):
CATransaction completion being called immediately

Resources