I have something like 30 annotations in my map and I want speed up the dropping animation.
Is it possible speed up the drop of annotation in MKMapView or drop all of them at once?
You'll need to implement your own drop animation in the didAddAnnotationViews delegate method. You should also set animatesDrop to NO to avoid a possible double animation.
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews
{
NSTimeInterval delayInterval = 0;
for (MKAnnotationView *annView in annotationViews)
{
CGRect endFrame = annView.frame;
annView.frame = CGRectOffset(endFrame, 0, -500);
[UIView animateWithDuration:0.125
delay:delayInterval
options:UIViewAnimationOptionAllowUserInteraction
animations:^{ annView.frame = endFrame; }
completion:NULL];
delayInterval += 0.0625;
}
}
This drops the annotations at a rate you specify.
To drop them all at once, hard-code the delay parameter to 0.0 instead of the incrementing delayInterval.
Related
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.
I have around 500 pins I would like to display on a MKMapView.
However, when I call mapView.addAnnotations(places) (places being an array of MKAnnotation objects), the pins slowly drop one by one.
I would like to either :
drop all pins at once at load
cancel the drop animation altogether
Is this possible ?
You should set animatesDrop property of your MKAnnotationViews to NO(false)
By setting animation property to No will drop all the Pins at once on the MapView.
MKAnnotationView *annotationView =[[MKAnnotationView alloc]init];
annotationView.animatesDrop=FALSE;
You'll need to implement your own drop animation in the didAddAnnotationViews delegate method. You should also set animatesDrop to NO to avoid a possible double animation.
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews
{
NSTimeInterval delayInterval = 0;
for (MKAnnotationView *annView in annotationViews)
{
CGRect endFrame = annView.frame;
annView.frame = CGRectOffset(endFrame, 0, -500);
[UIView animateWithDuration:0.125
delay:delayInterval
options:UIViewAnimationOptionAllowUserInteraction
animations:^{ annView.frame = endFrame; }
completion:NULL];
delayInterval += 0.0625;
}
}
When implementing UIView animateWithDuration:animations:completion: Class method, I encountered with a scenario I couldn't figure our how to handle.
I have a UIImageView that moves across the screen. Upon a certain event, it should change direction, and move to a new position. But(!) It seems that if this event happens, instead of just changing directions and moving to the new position, it 'jumps' to the original 'end position' and begin moving to the new position from there.
I'm not sure how I can instruct the 'moving object', upon completion == NO, to capture its current position and start animating from there, instead of jumping to the predetermined end position.
Here is the code:
- (IBAction)goHere:(UIButton *)sender
{
CGPoint movingEndPoint;
if ( sender == self.btn1 )
{
movingEndPoint = CGPointMake( sender.center.x + 40, sender.center.y + 40 );
}
if ( sender == self.btn2 )
{
movingEndPoint = CGPointMake( sender.center.x - 40, sender.center.y - 40 );
}
[UIView animateWithDuration:3
animations:^ {
self.movingObj.center = movingEndPoint;
}
completion:^(BOOL completion) {
if (completion == NO)
{
//How to express "New Position = Current Position"?
}
}];
}
This behavior has been remedied in iOS 8 (where it picks up the new animation using the object's current position and direction), but in earlier iOS versions, the easiest fix is to use the rendition of animateWithDuration with the options parameter, and include the UIViewAnimationOptionBeginFromCurrentState option.
Alternatively, you can use the view's layer's presentationLayer to get the current position mid-animation. For example,
CALayer *layer = self.movingObj.layer.presentationLayer;
CGRect currentFrame = layer.frame;
You can then set then stop the animations (e.g. [self.movingObj.layer removeAllAnimations]) and then set the frame of the view using this currentFrame before initiating the next animation.
Instead of doing it in completion block,
add this code in your event before giving new position.
Lets say you event method is eventMethod,
your modified code should be
[UIView animateWithDuration:5
animations:^ {
self.movingObj.center = CGPointMake(400, 400); //old final position
}
completion:^(BOOL completion) {
}];
-(void)eventMethod{
_movingObj.frame =[_movingObj.layer.presentationLayer frame];
[UIView animateWithDuration:2
animations:^ {
self.movingObj.center = CGPointMake(0, 0); //new final position
}
completion:^(BOOL completion) {
}];
}
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;
}];
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a UILabel which i added a pan gesture recognizer to and I also have a trashcan image on my view using the UIImage view. I want to delete the UILabel from my program view every time i drag the UILabel to the trashcan image.
I assume you want to do something like this:
I'll show you how to implement that.
We're going to need an outlet for the label and an outlet for the trashcan view:
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIImageView *trashView;
#property (strong, nonatomic) IBOutlet UILabel *label;
#end
Connect these to your label and your trashcan view. We'll also need two instance variables:
#implementation ViewController {
CGPoint labelOriginalCenter;
BOOL trashIsShowingPendingDropAppearance;
}
We need to save the original position of the label, so we can animate it back there if the drag is cancelled:
- (void)viewDidLoad {
[super viewDidLoad];
labelOriginalCenter = self.label.center;
}
Now let's make the action for the pan gesture recognizer. We need to move the label based on the gesture. Then we need to take action based on the state of the gesture.
- (IBAction)labelWasDragged:(UIPanGestureRecognizer *)recognizer {
[self moveLabelForDrag:recognizer];
switch (recognizer.state) {
case UIGestureRecognizerStateChanged:
[self labelDragDidChange:recognizer];
break;
case UIGestureRecognizerStateEnded:
[self labelDragDidEnd:recognizer];
break;
case UIGestureRecognizerStateCancelled:
[self labelDragDidAbort:recognizer];
break;
default:
break;
}
}
To move the label, we change its center by the gesture's translation. We also reset the gesture's translation to zero each time it changes.
- (void)moveLabelForDrag:(UIPanGestureRecognizer *)sender {
CGPoint translation = [sender translationInView:self.label];
[sender setTranslation:CGPointZero inView:self.label];
CGPoint center = self.label.center;
center.x += translation.x;
center.y += translation.y;
self.label.center = center;
}
If the gesture changed, we want to update the appearance of the trash can based on whether the touch is over the trash can:
- (void)labelDragDidChange:(UIPanGestureRecognizer *)recognizer {
if ([self dragIsOverTrash:recognizer]) {
[self updateTrashAppearanceForPendingDrop];
} else {
[self updateTrashAppearanceForNoPendingDrop];
}
}
If the gesture ended, we want to throw away the label, or abort the drag, based on whether the touch was over the trash can when the gesture ended:
- (void)labelDragDidEnd:(UIPanGestureRecognizer *)recognizer {
if ([self dragIsOverTrash:recognizer]) {
[self dropLabelInTrash];
} else {
[self abortLabelDrag];
}
}
If the gesture was cancelled, we want to abort the drag:
- (void)labelDragDidAbort:(UIPanGestureRecognizer *)recognizer {
[self abortLabelDrag];
}
To detect whether the gesture's touch is over the trash can, we ask the gesture recognizer for its location in the trash view's coordinate system. Then we ask the trash view whether that location is inside the trash view's bounds.
- (BOOL)dragIsOverTrash:(UIPanGestureRecognizer *)recognizer {
CGPoint pointInTrash = [recognizer locationInView:self.trashView];
return [self.trashView pointInside:pointInTrash withEvent:nil];
}
We could update the trash can's appearance in lots of different ways. Here, we'll make the trash can wiggle while the drag is over the trash can:
- (void)updateTrashAppearanceForPendingDrop {
if (trashIsShowingPendingDropAppearance)
return;
trashIsShowingPendingDropAppearance = YES;
self.trashView.transform = CGAffineTransformMakeRotation(-.1);
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
self.trashView.transform = CGAffineTransformMakeRotation(.1);
} completion:nil];
}
When the drag moves off the trash can, we need to make the trash can stop wiggling:
- (void)updateTrashAppearanceForNoPendingDrop {
if (!trashIsShowingPendingDropAppearance)
return;
trashIsShowingPendingDropAppearance = NO;
[UIView animateWithDuration:0.15 animations:^{
self.trashView.transform = CGAffineTransformIdentity;
}];
}
When we want to drop the label in the trash, we need to do several things. We need to stop the trash can wiggling, we need to animate the label into the trash can, and when the animation ends, we need to remove the label entirely.
- (void)dropLabelInTrash {
[self updateTrashAppearanceForNoPendingDrop];
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.label.center = self.trashView.center;
self.label.transform = CGAffineTransformMakeScale(0.1, 0.1);
} completion:^(BOOL finished) {
[self.label removeFromSuperview];
self.label = nil;
}];
}
If the drag was aborted, we need to stop the trash can wiggling and animate the label back to its original position:
- (void)abortLabelDrag {
[self updateTrashAppearanceForNoPendingDrop];
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.label.center = labelOriginalCenter;
} completion:nil];
}
That's all!
The label can be gotten from the gesture recognizer's view property. As far as the trash can, you could use CGRectIntersectsRect to determine if your dragged label's rect overlaps the trashcan's rect. Something like this within the gesture recognizer's action method:
- (IBAction)handlePan:(UIPanGestureRecognizer *)sender {
// your other panning code here
if (sender.state == UIGestureRecognizerStateEnded){
if (CGRectIntersectsRect(sender.view.frame, trashcanImageView.frame))
[ sender.view removeFromSuperview];
}
}