Creating Youtube Style Loading Animation
Custom loading animation for you apps
Overview
In this tutorial we will be building a shimmer animation which can be used as a placeholder view for time when your app is busy fetching data through API or some background process.
This type of loading was introduced by Facebook and now commonly seen in apps like Youtube and Instagram as well.
If you want your app to stand out, this is the new normal.
What are we building ?
A custom animation view, which can act like a placeholder till the time API returns response.
Our final product will look something like this:
Getting Started
Download the Final Project from here.
Let’s look into the project. For creating shimmer effect we will be working with GradientLayer
and CABasicAnimation
Extend UIView
and add the below code:
extension UIView {
func applyShimmerEffect() {//1let light = backgroundColor?.withAlphaComponent(0.5).cgColor ?? UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColorlet dark = UIColor.clear.cgColorlet shimmerGradientLayer = CAGradientLayer()shimmerGradientLayer.colors = [light, dark]shimmerGradientLayer.frame = CGRect(
x: -bounds.width,
y: 0,
width: 50 * bounds.size.width,
height: bounds.size.height
)shimmerGradientLayer.startPoint = CGPoint(x: 0.0, y: 0)
shimmerGradientLayer.endPoint = CGPoint(x: 1.0, y: 0)
shimmerGradientLayer.locations = [0.0, 0.1]
layer.mask = shimmerGradientLayer//2let animation = CABasicAnimation(keyPath: "locations")animation.fromValue = [0.0, 0.1]animation.toValue = [0.1, 0.2]animation.duration = 0.7animation.repeatCount = Float.infinityanimation.autoreverses = trueshimmerGradientLayer.add(animation, forKey: "shimmer")}}
//1
We will start with creating two colours for our gradient layer. First one is Light
which has colour of view with alpha of 0.5
, second is a clear
colour.
Create a gradient layer, add the above colours to it. Set the frame of gradient to something bigger in larger in width and placed at the a position -bounds.width
.This is done to give a pulse effect when we animate the layer.
Our gradient should animate along xaxis
so the start
and end
points are given accordingly.
location
property of the gradient is animatable. We will be changing the location property to produce animation.
//2
Create a CABasicAnimation
instance. We will provide toValue
and fromValue
to the instance, toValue
should be the location property set by our gradient layer and fromValue
can be according to our animation.
We are setting the repeat count to infinity
as our motive is to show the animation until API returns the response. Also, to make the animation smooth and have a pulse effect, we will set autoreverses = true
.
This is all we need to create a shimmer effect. Next step. Creating shimmer Cards.
Card View:
We will need a protocol Shimmerable
for creating shimmer cards, add the following code —
protocol Shimmerable: UIView {var shimmerViews: [UIView] { get }}extension Shimmerable {func loadShimmer() {shimmerViews.forEach { $0.applyShimmerEffect() }}
Our card view will conform to this protocol, and will provide it the views on which shimmer needs to be applied.
Note: Our shimmer card is something which will be shown during API call. It is a placeholder which will be displayed till the time data is fetched.
Let’s create a shimmer view card, Add the following class and we will go step by step to understand it.
final class ShimmerView: UIView {override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
backgroundColor = .clear
}required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")}//1
private let titleView: UIView = {
let view = UIView()
view.layer.cornerRadius = 5
view.width(160)
view.height(25)
view.backgroundColor = .lightGray
return view
}()private let descriptionView: UIView = {
let view = UIView()
view.layer.cornerRadius = 5
view.width(UIScreen.main.bounds.width * 0.80)
view.height(150)
view.backgroundColor = UIColor.red.withAlphaComponent(0.5)
return view
}()}//2
extension ShimmerView {func commonInit() {
setupUI()
setConstraints()
}func setupUI() {
addSubview(titleView)
addSubview(descriptionView)
}func setConstraints() {
titleView.leadingToSuperview(offset: 15)
titleView.topToSuperview(offset: 15)
descriptionView.leadingToSuperview(offset: 15)
titleView.bottomToTop(of: descriptionView, offset: -12)
}
}//3
extension ShimmerView: Shimmerable {
public var shimmerViews: [UIView] {
return [titleView, descriptionView]
}
}
//1
We have created a ShimmerView
class which is subclasses of UIView
. We have add two views, title
and description
, on which we will be applying the shimmer effect.
//2
this extension just adds the view to superView
and align them one below the other.
//3
We will now conform to protocol Shimmerable
and provide the views to which we will be adding shimmer effect, in our case, title
and description
view.
Let’s add and connect this view to our ViewController
. Open ViewController.swift
and add the following code in viewDidLoad
method—
override func viewDidLoad() {super.viewDidLoad()view.addSubview(shimmerView)
shimmerView.topToSuperview(offset: 20, usingSafeArea: true)
shimmerView.width(UIScreen.main.bounds.width)
shimmerView.centerXToSuperview()DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.shimmerView.loadShimmer()
}
}
Build and Run ! Also, let’s see how it looks in Dark Mode.
Awesome !! We have a placeholder for our content now and we got rid of our traditional loading style. Do you know, SwiftUI has an in-build placeholder for its properties ? well that’s a good topic for another animation tutorial.