Simplifying UIKit Animations

If you have ever worked with animations in iOS using UIKit you have most likely ended up with nested animation blocks and intractable code at some point. One of the problems that comes up is trying to chain multiple animations. You want animation B to run only after animation A has completed, and maybe then do something when animation B completes. This would look something like

[UIView animateWithDuration:0.25 animations:^() {
    // animation A
} completion:^(BOOL finished) {
    if (finished) {
        [UIView animateWithDuration:0.25 animations:^() {
            // animation B
        } completion:^(BOOL finished) {
            // Other code
        }];
    }
}];

And then you may want animations C and D to run after that, so much for clean code.

Another problem with this approach is that you can't easily switch or manipulate the animations. For instance you may want to run animation B after animation A in some cases and animation C after animation A in some other cases. Or you may want to dynamically change the properties of the animation like the duration.

A good solution to this problem is to make each animation an object. In other words treat animations as first-class citizens. This way the previous example becomes

AIAnimation* animA = [AIAnimation animationWithDuration:0.25  
    block:^() {
        // animation A
    }];

AIAnimation* animB = [AIAnimation animationWithDuration:0.25  
    block:^() {
        // animation B
    }];
animB.completionBlock = ^(BOOL finished) {  
        // Other code
    };

AIAnimationSequence* seq = [AIAnimationSequence animationSequenceWithAnimations:@[ animA, animB ]];  
[seq run];

You can also pass animations around as arguments, copy animations, and so on. Here is another example:

- (AIAnimation*)createAnimation {
  AIAnimation* animA = [AIAnimation animationWithDuration:0.25
      block:^() {
          // animation
      }];
  animB.easeIn = YES
  animA.easeOut = NO;

  AIAnimation* animB = [animA copy];
  animB.easeIn = NO
  animB.easeOut = NO;

  AIAnimation* animC = [animA copy];
  animC.easeIn = NO
  animC.easeOut = YES;

  return [AIAnimationSequence
      animationSequenceWithAnimations:@[ animA, animB, animC ]];
}

My implementation of animation objects includes support for animation curves (easing) and other animation properties. It also has three blocks: set up, animation and completion. The full code is in the AIAnimation GitHub project page. You can also just add it to your project via CocoaPods with pod 'AIAnimation'.