Moving an image along a series of CGPoints

2019-05-02 00:15发布

问题:

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

回答1:

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];
}


回答2:

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 UIViews animations manage things in other threads so that's probably why the for statement is not working.