Search code examples
iosobjective-cuiviewcore-graphicscore-animation

iOS: How to animate an image along a sine curve?


As in the title stated, I want to to animate some bubbles that travel along a sine curve from the bottom to the top within an UIView using CG/CA or even OpenGL if necessary.

Here is my CA code snippet that works fine, but it's a straight line which is animated. How can I build in the sine curve behaviour ?

- (void)animateBubble:(UIImageView *)imgView {
    [UIView beginAnimations:[NSString stringWithFormat:@"%i",imgView.tag] context:nil];
    [UIView setAnimationDuration:6];
    [UIView setAnimationDelegate:self];
    imgView.frame = CGRectMake(imgView.frame.origin.x, 0, imgView.frame.size.width, imgView.frame.size.height);
    [UIView commitAnimations];
}

I've already achieved the wanted result with SpriteKit (have a look: http://youtu.be/Gnj3UAD3gQI). The bubbles travel along a sine curve from bottom to the top where the amplitude is within a random range. Moreover, I use the gyroscope sensors to additionally influence the path.

Is this behaviour somehow reproducible with UIKit, CG, CA (if absolutely necessary also with OpenGL) ? Code samples would be wonderful, but any other ideas are also highly appreciated.


Solution

  • To animate along a path, you first need to define that path. If you really need a true sine curve, we can show you how to do that, but it's probably easiest to define something that approximates a sine curve using two quadratic bezier curves:

    CGFloat width = ...
    CGFloat height = ...
    CGPoint startPoint = ...
    
    CGPoint point = startPoint;
    CGPoint controlPoint = CGPointMake(point.x + width / 4.0, point.y - height / 4.0);
    CGPoint nextPoint = CGPointMake(point.x + width / 2.0, point.y);
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:point];
    [path addQuadCurveToPoint:nextPoint controlPoint:controlPoint];
    
    point = nextPoint;
    controlPoint = CGPointMake(point.x + width / 4.0, point.y + height / 4.0);
    nextPoint = CGPointMake(point.x + width / 2.0, point.y);
    
    [path addQuadCurveToPoint:nextPoint controlPoint:controlPoint];
    

    That renders path like so:

    enter image description here

    Obviously, change startPoint, width, and height to be whatever you want. Or repeat that process of adding more bezier paths if you need more iterations.

    Anyway, having defined a path, rather than rendering the path itself, you can create a CAKeyframeAnimation that animates the position of the UIView along that path:

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.fillMode = kCAFillModeForwards;
    animation.removedOnCompletion = NO;
    animation.duration = 1.0;
    animation.path = path.CGPath;
    [view.layer addAnimation:animation forKey:@"myPathAnimation"];