I have path stored in an array of CGPoints which I'd like to move an image along. Here's the general code I have so far:
-(void)movePic:(id)sender{
for(int i = 0; i < self.array.count; i++){
CGPoint location = [[self.array objectAtIndex:i] CGPointValue];
[UIView animateWithDuration:0.1 animations:^{
self.imageView.center = location;
} completion:^(BOOL finished){
}];
}
}
The problem is the for loop runs extremely fast, so you only see the animation on the last points. I'm unsure of how to better design this. Ideally, what could I do to make sure one animation finishes before the other begins? Should I not use a for loop? Thanks
Your code assumes that UIView animations run synchronously in the main thread, which they do not.
You seem to have two options
- Explicit
CAKeyframeAnimation
for animating a CALayer
along any amount of sample points (interpolated between them)
- Implicit recursive
UIView
animation for animating a UIView
along a series of sample points (interpolated between them)
The former would be much more efficient - still I thought I oould show you both options.
CAKeyframeAnimation
- (void)movePic:(id)sender
{
//create a mutable core-graphics path
CGMutablePathRef path = CGPathCreateMutable();
for(int i = 0; i < self.array.count; i++)
{
CGPoint location = [[self.array objectAtIndex:index] CGPointValue];
CGPathAddLineToPoint(path, nil, location.x, location.y);
}
//create a new keyframe animation
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//add our path to it
pathAnimation.path = path;
//be nice to the system
CGPathRelease(path);
//setup some more animation parameters
pathAnimation.duration = 0.1 * self.array.count;
//add the animation to our imageView's layer (which will start the animation)
[self.imageView.layer addAnimation:pathAnimation forKey:@"pathAnimation"];
}
UIView Animation
- (void)movePicToPointAtIndex:(unsigned int)index
{
//safeguard check...
if ([self.array count] <= index)
return;
//get the next location
CGPoint location = [[self.array objectAtIndex:index] CGPointValue];
//animate the imageView center towards that location
[UIView animateWithDuration:0.1
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.imageView.center = location;
} completion:^(BOOL finished){
//we are done with that animation, now go to the next one...
[self movePicToPointAtIndex:index+1];
}];
}
- (void)movePic:(id)sender
{
[self movePicToPointAtIndex:0];
}
Ok, the thing you have to do is set the array of points as a property of the class, something like animationPath
. Ok, so now you would have to pay attention to the delegate methods of the UIView animation delegate methods (it's not actually a different class, it's just a delegate of the class' methods).
Set a method to call on setAnimationDidStopSelector:selector
every time the animation stops, so here you would have something like this:
//Inside the callback for setAnimationDidStopSelector
if ([animationPath count] != 0){
//Go to next point
CGPoint location = [[self.array objectAtIndex:0] CGPointValue];
[UIView animateWithDuration:0.1 animations:^{
self.imageView.center = location;
} completion:^(BOOL finished){
}];
}
else{
NSLog(@"Nowhere else to go, animation finished :D");
}
So just be sure to fire your animation with the first point.
As far as I remember UIView
s animations manage things in other threads so that's probably why the for statement is not working.