Whats wrong with animations?

CAAnimations and UIView animations are extremely powerful, but it is difficult to chain multiple animations together, especially while changing anchor points.

Furthermore, complicated animations are difficult to read.

Say I want to move myView 50 pixels to the right with spring and then change the background color with inward easing when the movement has finished:

The Old Way

    [UIView animateWithDuration:1.0
                          delay:0.0
         usingSpringWithDamping:0.8
          initialSpringVelocity:1.0
                        options:0 animations:^{
                            CGPoint newPosition = self.myView.frame.origin;
                            newPosition.x += 50;
                            self.myView.frame.origin = newPosition;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.5
                              delay:0.0
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:^{
            self.myView.backgroundColor = [UIColor purpleColor];
        } completion:nil];
    }];

Thats pretty gross huh... With JHChainableAnimations it is one line of code.

The New Way (JHChainableAnimations!!!)

self.myView.moveX(50).spring.thenAfter(1.0).makeBackground([UIColor purpleColor]).easeIn.animate(0.5);

There are also a lot of really good animation libraries out there such as RBBAnimation, DCAnimationKit, and PMTween, but they still fall short of having powerful chainable animations AND easy to read/write syntax.

Usage

Either clone the repo and manually add the Files in JHChainableAnimations or add the following to your Podfile

pod 'JHChainableAnimations', '~> 1.2.0'

Then just import the following header.

#import "JHChainableAnimations.h"

This is all a UIView category, so these chainables can be used on any UIView in a file where the header is imported.

Notes on using JHChainableAnimations with Swift can be found here.

Notes on using JHChainableAnimations with Auto Layout can be found here.

Animating

Chainable properties like moveX(x) must come between the view and the animate(t) function

Below is an example of how to double an objects size over the course of one second.

view.makeScale(2.0).animate(1.0);

If you want to move the view while you scale it, add another chainable property. Order is not important

view.makeScale(2.0).moveXY(100, 50).animate(1.0);
// the same as view.moveXY(100, 50).makeScale(2.0).animate(1.0);

A full list of chainable properties can be found here

Chaining Animations

To chain animations seperate the chains with the thenAfter(t) function.

Below is an example of how to scale and object for 0.5 seconds, and then move it for 1 second when that is done.

view.makeScale(2.0).thenAfter(0.5).moveXY(100, 50).animate(1.0);

Animation Effects

To add an animation effect, call the effect method after the chainable property you want it to apply to.

Below is an example of scaling a view with a spring effect.

view.makeScale(2.0).spring.animate(1.0);

If you add 2 to the same chainable property the second will cancel the first out.

view.makeScale(2.0).bounce.spring.animate(1.0);
// The same as view.makeScale(2.0).spring.animate(1.0);

A full list of animation effect properties can be found here

Anchoring

To anchor your view call an achoring method at some point in an animation chain. Like effects, calling one after another in the same chain will cancel the first out.

Below is an example of rotating a view around different anchor points

view.rotate(180).anchorTopLeft.thenAfter(1.0).rotate(90).anchorCenter.animate(1.0);

// view.rotate(90).anchorTopLeft.anchorCenter == view.rotate(90).anchorCenter

A full list of anchor properties can be found here

Delays

To delay an animation call the wait(t) or delay(t) chainable property.

Below is an example of moving a view after a delay of 0.5 seconds

view.moveXY(100, 50).wait(0.5).animate(1.0);
// The same as view.moveXY(100, 50).delay(0.5).animate(1.0);

Completion

To run code after an animation finishes set the animationCompletion property of your UIView or call the animateWithCompletion(t, completion) function.

view.makeX(0).animateWithCompletion(1.0, JHAnimationCompletion(){
    NSLog(@"Animation Done");
});

Is the same as:

view.animationCompletion = JHAnimationCompletion(){
    NSLog(@"Animation Done");
};
view.makeX(0).animate(1.0);

Is the same as:

view.makeX(0).animate(1.0).animationCompletion = JHAnimationCompletion(){
    NSLog(@"Animation Done");
};

Bezier Paths

You can also animate a view along a UIBezierPath. To get a bezier path starting from the views position, call the bezierPathForAnimation method. Then add points or curves or lines to it and use it in a chainable property.

UIBezierPath *path = [view bezierPathForAnimation];
[path addLineToPoint:CGPointMake(25, 400)];
[path addLineToPoint:CGPointMake(300, 500)];
view.moveOnPath(path).animate(1.0);

Animation effects do not work on path movements.

Semantics

I included a chainable property called seconds that is there purely for show. It does however, make the code a little more readable (if you're into that sort of thing).

view.makeScale(2.0).thenAfter(0.5).seconds.moveX(20).animate(1.0);
// view.makeScale(2.0).thenAfter(0.5).moveX(20).animate(1.0);

Using with Swift

Using JHChainableAnimations with Swift is a little different. Every chainable property must have () between the name and the parameters.

// swift code
view.makeScale()(2.0).spring().animate()(1.0);
// is the same as 
// view.makeScale(2.0).spring.animate(1.0);
// in Objective-C

Masonry, which uses a similar chainable syntax eventually made SnapKit to make get rid of this weirdness. That may be on the horizon.

Draveness copied my code into swift and it looks pretty good. DKChainableAnimationKit

Using with Auto Layout

Typically frames and autolayout stuff shouldn't mix so use the makeConstraint and moveConstraint chainable properties with caution (i.e dont try and scale a view when it has a height and width constraint). These properties should only be used with color, opacity, and corner radius chainable properties because they dont affect the layers position and therfore won't affect constraints.

This was only added as a syntactically easy way to animate constraints. The code below will set the constant of topConstraint to 50 and then trigger an animated layout pass in the background.

// You have a reference to some constraint for myView
self.topConstraint = [NSLayoutConstraint ...];
...
self.myView.makeConstraint(self.topConstraint, 50).animate(1.0);

This does not support animation effects yet.

Chainable Properties

Property Takes a... Usage
- (JHChainableRect) makeFrame; CGRect view.makeFrame(rect).animate(1.0);
- (JHChainableRect) makeBounds; CGRect view.makeBounds(rect).animate(1.0);
- (JHChainableSize) makeSize; (CGFloat: width, CGFloat: height) view.makeSize(10, 20).animate(1.0);
- (JHChainablePoint) makeOrigin; (CGFloat: x, CGFloat: y) view.makeOrigin(10, 20).animate(1.0);
- (JHChainablePoint) makeCenter; (CGFloat: x, CGFloat: y) view.makeCenter(10, 20).animate(1.0);
- (JHChainableFloat) makeX; (CGFloat: f) view.makeX(10).animate(1.0);
- (JHChainableFloat) makeY; (CGFloat: f) view.makeY(10).animate(1.0);
- (JHChainableFloat) makeWidth; (CGFloat: f) view.makeWidth(10).animate(1.0);
- (JHChainableFloat) makeHeight; (CGFloat: f) view.makeHeight(10).animate(1.0);
- (JHChainableFloat) makeOpacity; (CGFloat: f) view.makeOpacity(10).animate(1.0);
- (JHChainableColor) makeBackground; (UIColor: color) view.makeBackground(color).animate(1.0);
- (JHChainableColor) makeBorderColor; (UIColor: color) view.makeBorderColor(color).animate(1.0);
- (JHChainableFloat) makeBorderWidth; (CGFloat: f) view.makeBorderWidth(3.0).animate(1.0);
- (JHChainableFloat) makeCornerRadius; (CGFloat: f) view.makeCornerRadius(3.0).animate(1.0);
- (JHChainableFloat) makeScale; (CGFloat: f) view.makeScale(2.0).animate(1.0);
- (JHChainableFloat) makeScaleX; (CGFloat: f) view.makeScaleX(2.0).animate(1.0);
- (JHChainableFloat) makeScaleY; (CGFloat: f) view.makeScaleY(2.0).animate(1.0);
- (JHChainablePoint) makeAnchor; (CGFloat: x, CGFloat: y) view.makeAnchor(0.5, 0.5).animate(1.0);
- (JHChainableFloat) moveX; (CGFloat: f) view.moveX(50).animate(1.0)
- (JHChainableFloat) moveY; (CGFloat: f) view.moveY(50).animate(1.0)
- (JHChainablePoint) moveXY; (CGFloat: x, CGFloat: y) view.moveXY(100, 50).animate(1.0)
- (JHChainableFloat) moveHeight; (CGFloat: f) view.moveHeight(50).animate(1.0)
- (JHChainableFloat) moveWidth; (CGFloat: f) view.moveWidth(50).animate(1.0)
- (JHChainableDegrees) rotate; (CGFloat: angle) #not radians! view.rotate(360).animate(1.0);
- (JHChainablePolarCoordinate) movePolar; (CGFloat: radius, CGFloat: angle) view.movePolar(30, 90).animate(1.0);
- (JHChainableBezierPath) moveOnPath; (UIBezierPath *path) view.moveOnPath(path).animate(1.0);
- (JHChainableBezierPath) moveAndRotateOnPath; (UIBezierPath *path) view.moveAndRotateOnPath(path).animate(1.0);
- (JHChainableBezierPath) moveAndReverseRotateOnPath; (UIBezierPath *path) view.moveAndReverseRotateOnPath(path).animate(1.0);

Animation Effects

A quick look at these funcs can be found here

These animation functions were taken from a cool keyframe animation library that can be found here

They are based off of JQuery easing functions that can be found here

Anchoring

Info on anchoring can be found here

To Do

I have gotten a ton of great suggestions of what to do next. If you think this is missing anything please let me know! The following is what I plan on working on in no particular order.

  • OSX port
  • Swift Port similar to SnapKit
  • 3D rotations / movement

Contact Info && Contributing

Feel free to email me at jhurray33@gmail.com. I'd love to hear your thoughts on this, or see examples where this has been used.

MIT License