Core & UIKit Animation

Dimitri James Tsiflitzis

CocoaheadsSKG

Why use animation in your apps?

  • Assist in learning
  • Highlights visual queues
  • Natural look & feel
  • Provides meaning
  • App effectiveness & performance

Architecture

Hardware

OpenGL

Core Animation

UIKit

GPU

Examples of animations you can perform on layers

Move

Scale

Text

Rotate

Transparency

Radius

Colour

Layers

CALayer

CAShapeLayer

CATiledLayer

CAScrollLayer

CATextLayer

CAReplicationLayer

Creating a layer

CALayer *myLayer = [CALayer layer];
myLayer.frame    = CGRectMake(0,0,width,height);
myLayer.position = CGPointMake(30.0,67.0);
myLayer.content  = mylogo;
[self.layer addSubLayer:myLayer];

Creating animations

Animation types

  • Implicit
  • Explicit

Implicit

  • Simply change a layer property
mario.position = CGPoint.init(x: 10.0, y: 10.0) 

Explicit

  • More direct / fine grained Control
  • Classes
    • CABasicAnimation
    • CAKeyframeAnimation
    • CAAnimationGroup

Explicit animation 

CAAnimation

CAAnimationGroup

CAPropertyAnimation

CATransition

CABasicAnimation

CAKeyframeAnimation

CABasicAnimation

  • Animation for a (stringly typed) key path
    • "position"
    • "transform.rotation.z"
    • "backgroundColor"
  • Set to & from properties
  • Add the animation to the desired layer to kick off the animation

CABasicAnimation

let animation         = CABasicAnimation.init(keyPath: "transform.rotation.z")
animation.fromValue   = 0.0
animation.toValue     = CGFloat(M_PI * 2.0)
animation.duration    = 5.0
animation.repeatCount = Float(UInt8.max)
view.layer.add(animation, forKey: "rotationAnimation")

CAKeyframeAnimation

  • More "advanced" behaviour
  • Animate across a "key frame" group
    • Path (CGPathRef)
    • Values (Array)

CAKeyframeAnimation

  • Layer transformations with CATransform3D
  • Can set the transform property

CAKeyframeAnimation

func pulseAnimation() -> CAKeyframeAnimation {
        
        let pulseAnimation = CAKeyframeAnimation()
        pulseAnimation.keyPath = "transform";
        pulseAnimation.values = [
            NSValue.init(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0)),
            NSValue.init(caTransform3D: CATransform3DMakeScale(1.03, 0.97, 1.0)),
            NSValue.init(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0)),
            NSValue.init(caTransform3D: CATransform3DMakeScale(0.97, 1.03, 1.0)),
            NSValue.init(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0))
        ]
        pulseAnimation.keyTimes     = 
[0.0, NSNumber.init(value:1.0 / 4.0), NSNumber.init(value:1.0 / 2.0), NSNumber.init(value:3.0 / 4.0), 1.0];
        pulseAnimation.repeatCount  = Float(NSIntegerMax);
        pulseAnimation.autoreverses = true
        
        return pulseAnimation;
    }

CAAnimationGroup

  • An object that allows multiple animations to be grouped and run concurrently.
  • 60fps

CAAnimationGroup

func wobble() {
  // 1
  var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation1.fromValue = ovalPathLarge.CGPath
  wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath
  wobbleAnimation1.beginTime = 0.0
  wobbleAnimation1.duration = animationDuration
 
  // 2
  var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath
  wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath
  wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration
  wobbleAnimation2.duration = animationDuration
 
  // 3
  var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath
  wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath
  wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration
  wobbleAnimation3.duration = animationDuration
 
  // 4
  var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path")
  wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath
  wobbleAnimation4.toValue = ovalPathLarge.CGPath
  wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration
  wobbleAnimation4.duration = animationDuration
 
  // 5
  var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
  wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3, 
				     wobbleAnimation4]
  wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration
  wobbleAnimationGroup.repeatCount = 2
  addAnimation(wobbleAnimationGroup, forKey: nil)
}

CAAnimationGroup

UIKit Animations

UIKit Animations

  • Wrapper around CALayer
  • Uses Core Animation under the hood
  • Very easy to use
  • Offer event handling
  • Less properties to animate than layers
  • No rounded corners
  • Easy for stock animations, drop down to Core Animations for more complex functionality

UIKit Animations prehistory

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30];
self.alpha = 0.5f;
[UIView commitAnimations];

UIKit Animations now

 [UIView animateWithDuration:0.1f animations:^{
     self.alpha = 0.5f;
 }];

UIKit Animations block based API

animateWithDuration:

                  animations:

animateWithDuration:

                   animations:

                   completion:

animateWithDuration:

                             delay:

                         options:

                   animations:

                   completion:

UIKit Animation options

  • Bit mask for behaviour
    • Autoreverse or repeating
    • Enabling user intercation
  • Animation curves
  • Beginning from current state

Why Core Animation?

#define DURATION (1.2)

@interface ViewController () {
 
     NSTimer   *animationTimer;
     NSInteger animationCount;
     NSInteger numberIterations;
     CGPoint   startPosition;
     CGPoint   endPosition;
}

@end

Why Core Animation?

@implementation ViewController

- (IBAction)doAnimation:(id)sender {

    animationTimer = [NSTimer
        scheduledTimerWithTimeInterval:1./60.
        target:self
        selector:@selector(updateAnimation:)
        userInfo:nil
        repeats:YES];

    animationCount   = 0;
    numberIterations = 60. * DURATION;
    startPosition    = self.targetView.frame.origin;

    if (50. == startPosition.x) {
        endPosition = self.bottomRight;
    } else {
        endPosition = CGPointMake(50., 50.);
    }
}

Why Core Animation?

- (void)updateAnimation:(id)timer {

     animationCount++;
 
    if (numberIterations == animationCount) {
        [animationTimer invalidate];
    }
 
    CGRect targetFrame  = self.targetView.frame;
    CGFloat endWeight   = 1. * animationCount / numberIterations;
    CGFloat startWeight = 1. - endWeight;
    targetFrame.origin  = CGPointMake(
        startWeight * startPosition.x + endWeight * endPosition.x,
        startWeight * startPosition.y + endWeight * endPosition.y);

    self.targetView.frame = targetFrame;
} 

iOS10 property animators

  • Create gesture based animations
  • Create interruptible animations

iOS10 property animators

let alphaChange = UIViewPropertyAnimator(duration: 0.3, dampingRatio: 0.6, 
animations: { [weak self] in
      self?.circleView.alpha = 0.0
})

// To run an animation
alphaChange.startAnimation()

//To pause an animator
alphaChange.pauseAnimation()

// For a gesture recognizer or delegate that reports every step 
// of its progress (e.g. UIPanGestureRecognizer or a ScrollViewDidScroll) you can just apply the percentage 
// directly to the UIViewPropertyAnimator object

@IBAction func handlePan(recognizer: UIPanGestureRecognizer) {

    let translation              = recognizer.translationInView(self.view)
    let translatedCenterY        = view.center.y + translation.y
    let progress                 = translatedCenterY / self.view.bounds.size.height
    alphaChange.fractionComplete = progress
}

Thank you!

Core & UIKit Animation

By tsif

Core & UIKit Animation

  • 223