Episode #122

Animating with POP

19 minutes
Published on June 5, 2014

This video is only available to subscribers. Get access to this video and 572 others.

In this episode I cover Facebook's new, shiny animation framework called Pop. With it we explore spring & decay animations that can make your apps feel more alive.

Episode Links

Moving a View with a Spring Animation

- (IBAction)tapView:(UITapGestureRecognizer *)recognizer {
    CGPoint location = [recognizer locationInView:self.view];

    POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];
    anim.toValue = [NSValue valueWithCGPoint:location];
    anim.springBounciness = 20;
    anim.springSpeed = 1;

    [self.imageView.layer pop_addAnimation:anim forKey:@"move"];
}

Animating Scale with a Spring Animation

- (IBAction)tap:(UITapGestureRecognizer *)recognizer {

    const CGFloat LargeSize = 256.0f;
    const CGFloat SmallSize = 64.0f;

    POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];

    if (self.imageView.frame.size.width >= LargeSize) {
        anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, SmallSize, SmallSize)];
    } else {
        anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, LargeSize, LargeSize)];
    }

    anim.springSpeed = 10;
    anim.springBounciness = 10;
    [self.imageView.layer pop_addAnimation:anim forKey:@"scale"];
}

Drag and Slide with Decay Animations

- (IBAction)pan:(UIPanGestureRecognizer *)recognizer {
    [self.imageView.layer pop_removeAnimationForKey:@"move"];
    [self.imageView.layer pop_removeAnimationForKey:@"slide"];

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
        case UIGestureRecognizerStateChanged: {
            CGPoint translation = [recognizer translationInView:self.view];
            CGPoint center = self.imageView.center;
            center.x += translation.x;
            center.y += translation.y;
            self.imageView.center = center;
            [recognizer setTranslation:CGPointZero inView:self.view];

            break;
        }

        case UIGestureRecognizerStateEnded: {
            POPDecayAnimation *anim = [POPDecayAnimation animationWithPropertyNamed:kPOPLayerPosition];
            anim.deceleration = 0.998;
            anim.velocity = [NSValue valueWithCGPoint:[recognizer velocityInView:self.view]];
            [self.imageView.layer pop_addAnimation:anim forKey:@"slide"];

            break;
        }

        case UIGestureRecognizerStateCancelled:
            break;

        default:
            break;
    }
}

Notice how we cancel the animations when dragging, so we don't conflict with other animations that may be running at the same time.

Animating Color and Bounds Change of a UIButton

In this example, we have a button that changes state, that might be used as a confirmation button.


- (void)tap:(UITapGestureRecognizer *)recognizer {
    _isToggled = !_isToggled;
    UIColor *targetColor;
    CGRect targetBounds;
    CGFloat targetRadius;

    if (_isToggled) {
        targetColor = [UIColor greenColor];
        targetBounds = CGRectMake(initialBounds.origin.x - 0.5 * initialBounds.size.width,
                                  initialBounds.origin.y + 0.25 * initialBounds.size.height,
                                  initialBounds.size.width * 2,
                                  initialBounds.size.height * .5);
        targetRadius = 10.0f;

    } else {
        targetColor = [UIColor blueColor];
        targetBounds = initialBounds;
        targetRadius = 36.0f;
    }

    // TODO animate
    POPSpringAnimation *colorAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBackgroundColor];
    colorAnim.toValue = (id)[targetColor CGColor];
    colorAnim.springSpeed = 6;
    [self.layer pop_addAnimation:colorAnim forKey:@"color"];

    POPSpringAnimation *boundsAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
    boundsAnim.toValue = [NSValue valueWithCGRect:targetBounds];
    boundsAnim.springSpeed = 6;
    [self.layer pop_addAnimation:boundsAnim forKey:@"bounds"];

    POPSpringAnimation *cornerAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerCornerRadius];
    cornerAnim.toValue = @(targetRadius);
    cornerAnim.springSpeed = 6;
    [self.layer pop_addAnimation:cornerAnim forKey:@"corner"];
}