Andrew Pouliot

Frame-by-frame animation with UIView

Sometimes the built-in animation curves in UIView (or Core Animation for that matter) just don't cut it.

You want to animate a bouncy ball with (somewhat) realistic physics, or just need to use an animation curve that's not ease-in, ease-out, ease-in/out, or linear. But writing a bunch of boilerplate code for setting up your CAKeyframeAnimations can really bog down the flow of your UI code.

Never fear, grab UIView+ExplicitAnimation from the repo on github.

simulator 212x400

Here's the relevant code for the demo:

CGPoint startCenter = ((CALayer *)[bouncyView.layer presentationLayer]).position;
CGPoint endCenter = bouncyViewCenter;

//Scope for variables in physics simulation
__block double v = -402;
__block double p = startCenter.y;
double elasticity = 0.7;
double g = 1080;
//Add an additional second for each 100 pixel of height we have
double duration = 2.0 + 0.01 * (endCenter.y - startCenter.y);

[UIView animateExplicitlyWithDuration:duration
                            frameRate:30.0
                           animations:^(double t, double dt) {
    DNUIViewExplicitAnimationProxy *objectProxy = [bouncyView explicitFrameProxy];
    DNUIViewExplicitAnimationProxy *shadowProxy = [shadowView explicitFrameProxy];

    //Physics simulation here :)
    //If we would surpass the target location, bounce instead
    if (p + dt * v > endCenter.y) {
        v = -elasticity * v;
        p = endCenter.y;
    } else {
        v += g * dt;
        p += dt * v;
    }
    objectProxy.alpha = 1.0 + 0.5 * sin(0.1 * t);
    objectProxy.center = (CGPoint) {.x = endCenter.x, .y = p};

    double d = endCenter.y - p;
    shadowProxy.alpha = 0.8 / (1.0 + 0.01 * d);
    shadowProxy.transform = CGAffineTransformMakeScale(1.0 + 0.005 * d,
                                                       1.0 + 0.005 * d);
}];

Note how simple it is and there's no boilerplate boxing/unboxing of floats or CGPoints.

I decided on the objectProxy business instead of swizzling -[UIView actionForLayer:forKey: to get the changes you want in the value of alpha or center or what have you. If you're less timid about swizzling, the proxy business could be eliminated.

More old posts