The iOS UI Kit Animation & Transition Advancements Study

Before iOS 10 becomes history, let’s take a look at what became a breakthrough in some of the crucial features that define further development in a lot of ways. On the last year’s WWDC, Apple introduced new ways of implementing animations to apps that could seriously improve the way animations look and more importantly, the way they feel in addition to the existing methods.  

The creation of interactive animations, as well as the interruptible animations in iOS 10 required new additional APIs. The major class is the open class UIViewPropertyAnimator : NSObject, UIViewImplicitlyAnimating, NSCopying.

iOS UI Kit Animation & Transition Advancements | Shakuro

Animator Creation and Animation Start Example

// setting up the timing parameters:

let timing   = UICubicTimingParameters(animationCurve: .easeInOut)

// creating the animator with the duration and timing parameters:

let animator = UIViewPropertyAnimator(duration: 3.0, timingParameters:timing)

// adding the animation block

animator.addAnimations {

self.animatedView.center = CGPoint(x: 400, y: self.animatedView.center.y)

}

// adding the Completion block that is executed after the animation completion:

animator.addCompletion {_ in

self.animatedView.backgroundColor = UIColor.red()

}

// starting the animator

animator.startAnimation()

The UIViewPropertyAnimator Class

The new UIViewPropertyAnimator class is fairly simple, casual and interruptible. The animations can be scrolled to any position and also played backwards. Due to this class, the animations become smoother and more natural.

UIViewPropertyAnimator supports 2 protocols:

public protocol UIViewAnimating : NSObjectProtocol

public protocol UIViewImplicitlyAnimating

Important Observable Properties

The iOS UI Kit Animation & Transition Advancements Study | Shakuro

animator.startAnimation () : active true false
animator.pauseAnimation ():   active false false
animator.isReversed = true : active false true
animator.startAnimation () : active true true
animator.stopAnimation (false*) stopped

* the false parameter means the animation will be stopped but with no ending:

stopAnimation(_ withoutFinishing: Bool)?. If the value is true, the state is inactive.

animator.finishAnimation (.current)
finishAnimation(at finalPosition: UIViewAnimatingPosition) finishes the animation on a certain position:
.end, .start, .current.

All the described manipulations are present on the state change chart below:

The iOS UI Kit Animation & Transition Advancements Study | Shakuro

To stop the animation, you can use TapRecognizer and depending on the state, perform actions:

switch animator.state {
case .active:
if animator.isRunning {
progressAnimator.pauseAnimation();
animator.pauseAnimation();
} else {
animator.startAnimation()
progressAnimator.startAnimation()
} default:
break
} }

To fast forward the progress, you can use UIPanGestureRecognizer and set the animator’s fractionComplete parameter:

func handleProgress (_ gr : UIPanGestureRecognizer) {
let s = gr.location(in: progress)
let f = min(s.x / progress.bounds.size.width, 1.0)
let fraction = max(0.0, f)
animator.fractionComplete = fraction
progressAnimator.fractionComplete = fraction
}

Time Curve

By default, Apple has four standard curves – easeOut, easeIn, easeInOut, linear.

You can enhance the animation with the help of the following classes:

UICubicTimingParameters – allows building the animation based on the two control points:

The iOS UI Kit Animation & Transition Advancements Study | Shakuro

let controlPoint1 = CGPoint(x: 0.2, y: 0.1)
let controlPoint2 = CGPoint(x: 0.8, y: 0.8)
let parameters = UICubicTimingParameters(controlPoint1: controlPoint1, controlPoint2: controlPoint2)
animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: parameters)

UISpringTimingParameters – adds springiness to the animation behavior with the help of various parameters like:

let mass: CGFloat = 2.0 // weight
let stiffness: CGFloat = 15.0 //elasticity
let damping: CGFloat = ...// point where the system comes to rest in the shortest period of time
let underDamping: CGFloat = damping * 0.5
let initialVelocity: CGVector = CGVector.zero 
let springParameters: UISpringTimingParameters = UISpringTimingParameters(mass: mass, stiffness: stiffness, damping: underDamping, initialVelocity: initialVelocity)
let customSpringParameters = UIViewPropertyAnimator(duration: 2.0, timingParameters: springParameters)
customSpringParameters.addAnimations {
  // animation block}

Using the springParameters in this case means that the duration value is ignored.

Interruptible Keyframe Animations

Setting up the interruptible frame animation:

animator.addAnimations { _ in
  UIView.animateKeyframes(withDuration: self.duration, delay: 0.0, options:[.calculationModeCubic]
 {_ in
  UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25) {
       self.squareView1.center = CGPoint(x: 50.0, y: 300.0)
        }
 UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25) {
       self.squareView1.center = CGPoint(x: 100.0, y: 100.0)
        }
 UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) {
          self.squareView1.center = CGPoint(x: 200.0, y: 300.0)
        }
 UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) {
          self.squareView1.center = CGPoint(x: 500.0, y: 400.0)
        }
} }

These APIs as well as the UIViewPropertyAnimator class were supposed to seriously boost the performance of apps with animations and smoothen the experience.

After a lot of testing and implementation done to some of the apps we worked on, these statement is totally proven. The fine tuned parameters and the predictive UI animations significantly improve the way animation is perceived in iOS.