Animation in iOS: native solutions and third-party frameworks

Today we will take a look at a couple of quite simple examples, learn about pros and cons of what Apple has to offer in terms of animation and get ourselves familiar with some third-party frameworks I personally find to be quite useful for handling animation. I will also show you how to use UIKit Dynamics and Motion Effects available starting from iOS 7.

UIKit Dynamics is a physical engine that allows you to change elements by adding behaviors, such as gravity, springs, forces, etc. And the engine takes care of everything else.

Motion Effects — allows you to create parallax effects that you, for example, see on the Home screen when slightly tilting your phone. It looks like Home screen is slightly moving in one direction while icons are moving into another. It creates an effect of depth you can easily implement in your own apps.

UIKit Dynamics

Let’s add UIView to the storyboard, make it gray and bind it using constraints. You’ve got to be attentive here since sometimes physical engine doesn’t handle this kind of binding well and you need to simply add an element somewhere in viewDidLoad() and specify its size using frame.

UIKit Dynamics storyboard

Next add several properties to ViewController class:

var animator: UIDynamicAnimator!
var gravity: UIGravityBehavior!
var collision: UICollisionBehavior!

Now we initiate this properties in viewDidLoad():

animator = UIDynamicAnimator(referenceView: view)
gravity = UIGravityBehavior(items:[box])
animator.addBehavior(gravity)

Start your animation in simulator to see how a grey square is falling somewhere behind the screen. Kinda fun but not much. Add UICollisionBehavior and assign true to translatesReferenceBoundsIntoBoundary. This property is responsible for system boundaries and if its value is true, views of your ViewController will be used as boundaries. Don’t forget to add the new behavior to animator object — it is responsible for all the calculations:

//add collision
collision = UICollisionBehavior(items:[box])
collision.translatesReferenceBoundsIntoBoundary =true
animator.addBehavior(collision)

Now the grey square is hitting the lower part of the screen — awesome!

Let’s go further and add a barrier. If barrier is added through the storyboard, the engine can’t calculate it precisely so we will add it manually:

//add barrier
let barrier = UIView(frame: CGRect(x:0.0, y:400.0, width:200.0, height:20.0))
barrier.backgroundColor = UIColor.blackColor()
view.addSubview(barrier)

The barrier is added and we need to add a collision:

let rightEdge = CGPoint(x: barrier.frame.origin.x + barrier.frame.size.width, y: barrier.frame.origin.y)
collision.addBoundaryWithIdentifier("barrier", fromPoint: barrier.frame.origin, toPoint: rightEdge)

UIKit Dynamics falling cube

Now everything is fine, the system works, the cube falls and collides. Let’s go further and add a little bit of elasticity to the cube:

let itemBehavior = UIDynamicItemBehavior(items:[box])
itemBehavior.elasticity =0.6
animator.addBehavior(itemBehavior)

Apart from elasticity we have a couple of other properties up our sleeve to play with:

  • elasticity
  • friction
  • density
  • resistance
  • angularResistance

We can also monitor the moment when the cube is meeting obstacles. We’re signing controller as a delegate:

collision.collisionDelegate = self

And implement the following method:

extension ViewController: UICollisionBehaviorDelegate {
 
    func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item: UIDynamicItem, withBoundaryIdentifier identifier:NSCopying?, atPoint p: CGPoint){
 
        //change box color, when it collideif let itemView = item as? UIView {
              itemView.backgroundColor = UIColor.redColor()}}}

And after each collision we will be changing cube’s color to red:

Changing color in UIKit Dynamics

Now let’s talk about what’s happening behind the scenes. Every behavior has action property. We can add a block of code our animation will execute during each step (about 400 milliseconds). Let’s add:

//add trails
        collision.action ={
            self.box.backgroundColor = UIColor.clearColor()
            let newBox = UIView(frame: self.box.frame)
            newBox.layer.borderWidth =1.0
            newBox.layer.borderColor = UIColor.lightGrayColor().CGColor
            newBox.transform = self.box.transform
            newBox.center = self.box.center
            self.view.addSubview(newBox)}

On every step of the animation a new cube will be added that is using coordinates of our grey cube while our “main character” is hidden so that we can enjoy the effect we’ve created :)

How UIKit Dynamics works

Here’s the final result:

UIKit Dynamics final result

Motion Effects

For the next example you’ll need a real device. Simulator won’t do — I’ve checked it myself: you can tilt your head or your monitor — it doesn’t help, I swear XD

Here’s our storyboard:

Motion Effects storyboard

Pay attention to constraints for backImView as well:

Motion Effects constraints for storyboard

It is done so that when backImView (image with the sky) shifts, it won’t show a white color of the view controller. And here’s the code:

override func viewDidLoad(){
        super.viewDidLoad()
 
        addMotionEffectToView(backImView, magnitude:50.0)
        addMotionEffectToView(frontImView, magnitude:-20.0)}
 
//MARK: Helpers
    func addMotionEffectToView(view: UIView, magnitude: CGFloat){
 
        let xMotion = UIInterpolatingMotionEffect(keyPath:"center.x", type: .TiltAlongHorizontalAxis)
        xMotion.minimumRelativeValue =-magnitude
        xMotion.maximumRelativeValue = magnitude
 
        let yMotion = UIInterpolatingMotionEffect(keyPath:"center.y", type: .TiltAlongVerticalAxis)
        yMotion.minimumRelativeValue =-magnitude
        yMotion.maximumRelativeValue = magnitude
 
        let group = UIMotionEffectGroup()
        group.motionEffects =[xMotion, yMotion]
 
        view.addMotionEffect(group)}

We will add motion effect to the back (backImView) and front view (frontImView). We aren’t interested in midImView here, it stays static.

Create a supplementary method called addMotionEffectToView and initialize it with your UIImageView with additional properties — and that’s it! Enjoy your parallax effect :)

Animated Popover with Transparent Background

The first step is to open the ViewController. The XIB we will be using in this example is called RateMeViewController and to open it, we run:

let vc = RateMeViewController(nibName:"RateMeViewController", bundle:nil)//// Make changes to vc (for example to modalPresentationStyle)//
self.present(vc, animated:true)

This would open the ViewController without any fancy animations, but we want more. So, to make the UI popover transparent, we need to set the modalPresentationStyle to .overFullScreen.

To avoid the blurry background swiping in from the bottom, we set the

modalTransitionStyle to .crossDisolve.

The result looks like this:

let vc = RateMeViewController(nibName:"RateMeViewController", bundle:nil)
vc.modalPresentationStyle = .overFullScreen
vc.modalTransitionStyle = .crossDissolve
self.present(vc, animated:true)

Before we can animate the popover view (the actual Rate window) into the screen, we first need to hide it. As the view is probably somehow constrained into the UIViewController, we will be using the bottom constraint to hide and make the View appear. To make a change to the bottom constraint, we need to reference it into our ViewController. Find the bottom constraint.

Tap on the constraint (to make it bold) and CTRL drag it into your UIViewController. The constraint type should be NSLayoutConstraint . Let’s name it bottomConstraint.

Next, we need to set the constraint to a value that hides the popover view. As the popover view is 350 pixels in height, moving the view to -500 pixels should definitely move it out of the screen. So, we add the following line to viewDidLoad() :

bottomConstraint.constant =-500

That’s all for hiding it.

Next, to make it appear again, we will be using the power of viewDidAppear, called once the Controller has finished animating in. We need to reset the bottomConstraint of the popover view back to what it was initially (50 in my case). Using bottomConstraint.constant = 50 would do the trick, but I promised an awesome animation. So we need the following block for the magic

UIView.animate(withDuration:0.6, delay:0,
              usingSpringWithDamping:0.7,
              initialSpringVelocity:0.3,
              options: .curveEaseOut, animations:{
   self.bottomConstraint.constant =50
   self.view.layoutIfNeeded()})

A short explanation of what is happening here. We are using the UIView animation function, which contains all animations in a block of code. As only changing the constant to a different value wouldn’t run the animation, we also need to run self.view.layoutIfNeeded(). The animation setup also contains a few parameters. First, we define a duration.

And that's done!

Cheetah

Personally, I like Cheetah framework. It is amazingly compact in terms of code and allows you to receive quite good results.

Open Cheetah_project. In storyboard you have one button we’re going to click at all times :) Its code is available below:

@IBAction func buttonAction(sender: AnyObject){
        box.cheetah
            .scale(1.5)
            .duration(1.0)
            .spring()
            .cornerRadius(20.0)
            .wait()
 
	//reset
            .scale(1.0)
            .cornerRadius(0.0)
            .duration(0.5)
 
            .run()}

After writing a couple of code lines we get animation that would take us much more time if we used standard methods. And, of course, the more code we have, the harder it is to change.

Animation in iOS  using Cheetah framework

I would also recommend you to check out Spring framework. Developers have prepared a very nice app you can use to explore capabilities of their product and even take a look at the code of the effect you’re interested in. DCAnimationKit is a very simple framework with a lot of ready-made decisions that is also worth checking out.

Don’t forget that these are ready-made solutions written with low-level C functions, such as CGAffineTransformMakeRotation(), and if you’re serious about animation, you’ve got to deal with them.

Or, at least, you’ve got to be familiar with higher level methods, such as:

  • UIView.animateWithDuration
  • UIView.animateKeyframesWithDuration
  • UIView.addKeyframeWithRelativeStartTime

And you should understand CABasicAnimation(), CAPropertyAnimation(), etc. as well.

But if you just need to quickly implement a certain function, then one of the more “lazy solutions” described above is what you actually need.

Need MVP development, iOS and Android apps or prototyping? Check out our portfolio and make an order today!