Performing iOS Animations On Views With UIKit And UIView

Spread the love
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Performing iOS Animations On Views With UIKit And UIViewPerforming iOS Animations On Views With UIKit And UIView

Saravanan V

2019-11-20T11:00:00+00:002019-11-25T05:05:39+00:00I have been an iOS developer for over a decade now and have rarely seen articles that consolidate all possible ways to perform animations in iOS. This article aims to be a primer on iOS animations with the intent of exhaustively covering the different ways of doing the same.

Given the extensiveness of the topic, we would cover each part succinctly at a fairly high level. The goal is to educate the reader with a set of choices to add animations to his/ her iOS app.

Before we start off with topics related to iOS, let us take a brief look at animation speed.

Animating At 60FPS

Generally in videos, each frame is represented by an image and the frame rate determines the number of images flipped in the sequence. This is termed as ‘frames per second’ or FPS.

FPS determines the number of still images flipped within a second, which literally means that the more the number of images/ frames, more details/ information are displayed in the video. This holds true for animations as well.

FPS is typically used to determine the quality of animations. There is a popular opinion that any good animation should run at 60fps or higher — anything less than 60fps would feel a bit off.

Do you want to see the difference between 30FPS and 60FPS? Check this!

Did you notice the difference? Human eyes can definitely feel the jitter at lower fps. Hence, it is always a good practice to make sure that any animation you create, adheres to the ground rule of running at 60FPS or higher. This makes it feel more realistic and alive.

Having looked at FPS, let’s now delve into the different core iOS frameworks that provide us a way to perform animations.

Core Frameworks

In this section, we will touch upon the frameworks in the iOS SDK which can be used for creating view animations. We will do a quick walk through each of them, explaining their feature set with a relevant example.

UIKit/ UIView Animations

UIView is the base class for any view that displays content in iOS apps.

UIKit, the framework that gives us UIView, already provides us some basic animation functions which make it convenient for developers to achieve more by doing less.

The API, UIView.animate, is the easiest way to animate views since any view’s properties can be easily animated by providing the property values in the block-based syntax.

In UIKit animations, it is recommended to modify only the animatable properties of UIVIew else there will be repercussions where the animations might cause the view to end up in an unexpected state.

animation(withDuration: animations: completion)

This method takes in the animation duration, a set of view’s animatable property changes that need to be animated. The completion block gives a callback when the view is done with performing the animation.

Almost any kind of animation like moving, scaling, rotating, fading, etc. on a view can be achieved with this single API.

Now, consider that you want to animate a button size change or you want a particular view to zoom into the screen. This is how we can do it using the UIView.animate API:

let newButtonWidth: CGFloat = 60

UIView.animate(withDuration: 2.0) { //1
self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) //2
self.button.center = self.view.center //3
}

Here’s what we are doing here:

We call the UIView.animate method with a duration value passed to it that represents how long the animation, described inside the block, should run.
We set the new frame of the button that should represent the final state of the animation.
We set the button center with its superview’s center so that it remains at the center of the screen.
The above block of animation code should trigger the animation of the button’s frame changing from current frame:

Width = 0, Height = 0

To the final frame:

Width = Height = newButtonWidth

And here’s what the animation would look like:

animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion

This method is like an extension of the animate method where you can do everything that you can perform in the prior API with some physics behaviors added to the view animations.

For example, if you want to achieve spring damping effects in the animation that we have done above, then this is how the code would look like:

let newButtonWidth: CGFloat = 60
UIView.animate(withDuration: 1.0, //1
delay: 0.0, //2
usingSpringWithDamping: 0.3, //3
initialSpringVelocity: 1, //4
options: UIView.AnimationOptions.curveEaseInOut, //5
animations: ({ //6
self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
self.button.center = self.view.center
}), completion: nil)

Here’s the set of parameters we use:

durationRepresents the duration of the animation determining how long the block of code should run.
delayRepresents the initial delay that we want to have before the start of the animation.
SpringWithDampingRepresents the value of the springy effect that we want the view to behave. The value must be between 0 to 1. The lower the value, the higher the spring oscillation.
velocityRepresents the speed at which the animation should start.
optionsType of animation curve that you want to apply to your view animation.
Finally, the block of code where we set the frame of the button that needs to be animated. It is the same as the previous animation.
And here’s what the animation would look like with the above animation configuration:

UIViewPropertyAnimator

For a bit more control over animations, UIViewPropertyAnimator comes handy where it provides us a way to pause and resume animations. You can have custom timing and have your animation to be interactive and interruptible. This is very much helpful when performing animations that are also interactable with user actions.

The classic ‘Slide to Unlock’ gesture and the player view dismiss/ expand animation (in the Music app) are examples of interactive and interruptible animations. You can start moving a view with your finger, then release it and the view will go back to its original position. Alternatively, you can catch the view during the animation and continue dragging it with your finger.

Following is a simple example of how we could achieve the animation using UIViewPropertyAnimator:

let newButtonWidth: CGFloat = 60
let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { //1
self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
self.button.center = self.view.center
}
animator.startAnimation() //2

Here’s what we are doing:

We call the UIViewProperty API by passing the duration and the animation curve.
Unlike both the above UIView.animate API’s, the animation won’t start unless you specify it by yourself i.e. you’re in full control of the complete animation process/ flow.
Now, let’s say that you want even more control over the animations. For example, you want to design and control each and every frame in the animation. There’s another API for that, animateKeyframes. But before we delve into it, let’s quickly look at what a frame is, in an animation.

What Is A frame?

A collection of the view’s frame changes/ transitions, from the start state to the final state, is defined as animation and each position of the view during the animation is called as a frame.

animateKeyframes

This API provides a way to design the animation in such a way that you can define multiple animations with different timings and transitions. Post this, the API simply integrates all the animations into one seamless experience.

Let’s say that we want to move our button on the screen in a random fashion. Let’s see how we can use the keyframe animation API to do so.

UIView.animateKeyframes(withDuration: 5, //1
delay: 0, //2
options: .calculationModeLinear, //3
animations: { //4
UIView.addKeyframe( //5
withRelativeStartTime: 0.25, //6
relativeDuration: 0.25) { //7
self.button.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.maxY) //8
}

UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) {
self.button.center = CGPoint(x: self.view.bounds.width, y: start.y)
}

UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) {
self.button.center = start
}
})

Here’s the breakdown:

durationCall the API by passing in the duration of the animation.
delayInitial delay duration of the animation.
optionsThe type of animation curve that you want to apply to your view animation.
animationsBlock that takes all keyframe animations designed by the developer/ user.
addKeyFrameCall the API to design each and every animation. In our case, we have defined each move of the button. We can have as many such animations as we need, added to the block.
relativeStartTimeDefines the start time of the animation in the collection of the animation block.
relativeDurationDefines the overall duration of this specific animation.
centerIn our case, we simply change the center property of the button to move the button around the screen.
And this is how the final animations looks like:

CoreAnimation

Any UIKit based animation is internally translated into core animations. Thus, the Core Animation framework acts as a backing layer or backbone for any UIKit animation. Hence, all UIKit animation APIs are nothing but encapsulated layers of the core animation APIs in an easily consumable or convenient fashion.

UIKit animation APIs don’t provide much control over animations that have been performed over a view since they are used mostly for animatable properties of the view. Hence in such cases, where you intend to have control over every frame of the animation, it is better to use the underlying core animation APIs directly. Alternatively, both the UIView animations and core animations can be used in conjunction as well.

UIView + Core Animation

Let’s see how we can recreate the same button change animation along with specifying the timing curve using the UIView and Core Animation APIs.

We can use CATransaction’s timing functions, which lets you specify and control the animation curve.

Let’s look at an example of a button size change animation with its corner radius utilizing the CATransaction’s timing function and a combination of UIView animations:

let oldValue = button.frame.width/2
let newButtonWidth: CGFloat = 60

/* Do Animations */
CATransaction.begin() //1
CATransaction.setAnimationDuration(2.0) //2
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)) //3

// View animations //4
UIView.animate(withDuration: 1.0) {
self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth)
self.button.center = self.view.center
}

// Layer animations
let cornerAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.cornerRadius)) //5
cornerAnimation.fromValue = oldValue //6
cornerAnimation.toValue = newButtonWidth/2 //7

button.layer.cornerRadius = newButtonWidth/2 //8
button.layer.add(cornerAnimation, forKey: #keyPath(CALayer.cornerRadius)) //9

CATransaction.commit() //10

Here’s the breakdown:

beginRepresents the start of the animation code block.
durationOverall animation duration.
curveRepresents the timing curve that needs to be applied to the animation.
UIView.animateOur first animation to change the frame of the button.
CABasicAnimationWe create the CABasicAnimation object by referring the cornerRadius of the button as the keypath since that’s what we want to animate. Similarly, if you want to have granular level control over the keyframe animations, then you can use the CAKeyframeAnimation class.
fromValueRepresents the starting value of the animation, i.e. the initial cornerRadius value of the button from where the animation must start off.
toValueRepresents the final value of the animation, i.e. the final cornerRadius value of the button where the animation must end.
cornerRadiusWe must set the cornerRadius property of the button with the final value of the animation else the button’s cornerRadius value will get auto-reverted to its initial value after the animation completes.
addAnimationWe attach the animation object that contains the configuration of the entire animation process to the layer by representing the Keypath for which the animation needs to be performed.
commitRepresents the end of the animation code block and starts off the animation.
This is how the final animation would look like:

This blog is a great read to help create more advanced animations as it neatly walks you through most of the Core Animation framework APIs with instructions guiding you through every step of the way.

UIKitDynamics

UIKit Dynamics is the physics engine for UIKit which enables you to add any physics behaviors like collision, gravity, push, snap, etc, to the UIKit controls.

UIKitDynamicAnimator

This is the admin class of the UIKit Dynamics framework that regulates all animations triggered by any given UI control.

UIKitDynamicBehavior

It enables you to add any physics behavior to an animator which then enables it to perform on the view attached to it.

Different kinds of behaviors for UIKitDynamics include:

UIAttachmentBehavior
UICollisionBehavior
UIFieldBehavior
UIGravityBehavior
UIPushBehavior
UISnapBehavior
The architecture of UIKitDynamics looks something like this. Note that Items 1 to 5 can be replaced with a single view.

Let us apply some physics behavior to our button. We will see how to apply gravity to the button so that it gives us a feeling of dealing with a real object.

var dynamicAnimator : UIDynamicAnimator!
var gravityBehavior : UIGravityBehavior!

dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1

gravityBehavior = UIGravityBehavior(items: [button]) //2
dynamicAnimator.addBehavior(gravityBehavior) //3

Here’s the breakdown:

UIKitDynamicAnimatorWe have created a UIKitDynamicAnimator object which acts as an orchestrator for performing animations. We have also passed the superview of our button as the reference view.
UIGravityBehaviorWe have created a UIGravityBehavior object and pass our button into the array elements on which this behavior is injected.
addBehaviorWe have added the gravity object to the animator.This should create an animation as shown below:Notice how the button falls off from the center (its original position) of the screen to the bottom and beyond.

We should tell the animator to consider the bottom of the screen to be the ground. This is where UICollisionBehavior comes into picture.var dynamicAnimator : UIDynamicAnimator!
var gravityBehavior : UIGravityBehavior!
var collisionBehavior : UICollisionBehavior!

dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1

gravityBehavior = UIGravityBehavior(items: [button]) //2
dynamicAnimator.addBehavior(gravityBehavior) //3

collisionBehavior = UICollisionBehavior(items: [button]) //4
collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5
dynamicAnimator.addBehavior(collisionBehavior) //6

UICollisionBehaviorWe have created a UICollisionBehavior object and passed along the button so that the behavior is added to the element.
translatesReferenceBoundsIntoBoundaryEnabling this property tells the animator to take the reference views boundary as the end, which is the bottom of the screen in our case.
addBehaviorWe have added collision behavior to the animator here.Now, our button should hit the ground and stand still as shown below:That’s pretty neat, isn’t it?Now, let us try adding a bouncing effect so that our object feels more real. To do that, we will use the UIDynamicItemBehavior class.var dynamicAnimator : UIDynamicAnimator!
var gravityBehavior : UIGravityBehavior!
var collisionBehavior : UICollisionBehavior!
var bouncingBehavior : UIDynamicItemBehavior!

dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1

gravityBehavior = UIGravityBehavior(items: [button]) //2
dynamicAnimator.addBehavior(gravityBehavior) //3

collisionBehavior = UICollisionBehavior(items: [button]) //4
collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5
dynamicAnimator.addBehavior(collisionBehavior) //6

//Adding the bounce effect
bouncingBehavior = UIDynamicItemBehavior(items: [button]) //7
bouncingBehavior.elasticity = 0.75 //8
dynamicAnimator.addBehavior(bouncingBehavior) //9

UIDynamicItemBehaviorWe have created a UIDynamicItemBehavior object and pass along the button so that the behavior is added to the element.
elasticityValue must be between 0-1, it represents the elasticity i.e. the number of times the object must bounce on and off the ground when it is hit. This is where the magic happens — by tweaking this property, you can differentiate between different kinds of objects like balls, bottles, hard-objects and so on.
addBehaviorWe have added collision behavior to the animator here.
Now, our button should bounce when it hits the ground as shown below:

This repo is quite helpful and shows all UIKitDynamics behaviors in action. It also provides source code to play around with each behavior. That, in my opinion, should serve as an extensive list of ways to perform iOS animations on views!

In the next section, we will take a brief look into the tools that will aid us in measuring the performance of animations. I would also recommend you to look at ways to optimize your Xcode build since it will save a huge amount of your development time.

Performance Tuning

In this section, we will look at ways to measure and tune the performance of iOS animations. As an iOS developer, you might have already used Xcode Instruments like Memory Leaks and Allocations for measuring the performance of the overall app. Similarly, there are instruments that can be used to measure the performance of animations.

Core Animation Instrument

Try the Core Animation instrument and you should be able to see the FPS that your app screen delivers. This is a great way to measure the performance/ speed of any animation rendered in your iOS app.

Drawing

FPS is vastly lowered in the app that displays heavy content like images with effects like shadows. In such cases, instead of assigning the Image directly to the UIImageView’s image property, try to draw the image separately in a context using Core Graphics APIs. This overly reduces the image display time by performing the image decompression logic asynchronously when done in a separate thread instead of the main thread.

Rasterization

Rasterization is a process used to cache complex layer information so that these views aren’t redrawn whenever they’re rendered. Redrawing of views is the major cause of the reduction in FPS and hence, it is best to apply rasterization on views that are going to be reused several times.

Wrapping Up

To conclude, I have also summed up a list of useful resources for iOS animations. You may find this very handy when working on iOS animations. Additionally, you may also find this set of design tools helpful as a (design) step before delving into animations.

I hope I have been able to cover as many topics as possible surrounding iOS animations. If there is anything I may have missed out in this article, please let me know in the comments section below and I would be glad to make the addition!

(dm, yk, il)

X ITM Cloud News

Ana

Next Post

How To Use FOMO To Increase Conversions

Mon Nov 25 , 2019
Spread the love           How To Use FOMO To Increase ConversionsHow To Use FOMO To Increase Conversions Suzanne Scacca 2019-11-21T11:00:00+00:002019-11-25T05:05:39+00:00Consumers are motivated by need and desire. And sometimes, just sometimes, they’re motivated by FOMO. That’s right: we can now add the ‘Fear Of Missing Out’ to the list of drivers that […]
X- ITM

Cloud Computing – Consultancy – Development – Hosting – APIs – Legacy Systems

X-ITM Technology helps our customers across the entire enterprise technology stack with differentiated industry solutions. We modernize IT, optimize data architectures, and make everything secure, scalable and orchestrated across public, private and hybrid clouds.

This image has an empty alt attribute; its file name is x-itmdc.jpg

The enterprise technology stack includes ITO; Cloud and Security Services; Applications and Industry IP; Data, Analytics and Engineering Services; and Advisory.

Watch an animation of  X-ITM‘s Enterprise Technology Stack

We combine years of experience running mission-critical systems with the latest digital innovations to deliver better business outcomes and new levels of performance, competitiveness and experiences for our customers and their stakeholders.

X-ITM invests in three key drivers of growth: People, Customers and Operational Execution.

The company’s global scale, talent and innovation platforms serve 6,000 private and public-sector clients in 70 countries.

X-ITM’s extensive partner network helps drive collaboration and leverage technology independence. The company has established more than 200 industry-leading global Partner Network relationships, including 15 strategic partners: Amazon Web Services, AT&T, Dell Technologies, Google Cloud, HCL, HP, HPE, IBM, Micro Focus, Microsoft, Oracle, PwC, SAP, ServiceNow and VMware

.

X ITM