Thanks for filling out our form!
Here's what happens next:

We'll write you back as soon as possible, right now we might be very busy saving the World. Give us a day or two, we'll contact you for sure.


In the meantime, perhaps you would like to take a look at one of our blog posts that explains

How do we work with our Clients
How we can help you with your business

In Touch,
Prograils Team

Prograils blog

Michał Majchrzycki on 18.04.18 in Mobile
Post reusable activity loading indicator spinner switt

How to make a reusable Activity Indicator with Swift

Spinners let people know that your app isn't frozen while loading content. Check out how to make one that can be used on many views and storyboards.

Progress Indicators, spinner or - using Apple nomenclature - Activity Indicator, is a good thing. Great for showing users that something is going on in the app, i.e. that downloading process has started. In Swift we have many ways to create or just add a spinner to our app. Today I’d like to show you, how to make spinner, that can be used on many views/storyboards. Let’s go!

As a junior developer, I’m trying to use most simple and stable solutions. My first spinner that I was using was Activity Indicator View added from Storyboard in Xcode. It’s great, but has some limitations.

First, if you have more than one view controller, you must create a single indicator for each view. And handle this in the code.

Second, if you want to block all view during an update, Activity Indicator isn’t enough.

Third, blocking Navigation Controller or TabBar Controller is just more work, if you’re using only Activity Indicator.

So, what to do? Obviously, there are many Pods with great spinners. In my products I'm using JGProgressHUD, but you can find on Github many, many other great solutions.

But, let’s stay simple. Just one spinner, just some lines of code.

So firstly, let’s make a new Swift file called simple Spinner.swift. We’ll use UIKit. Next, we should create a new class:

open class Spinner

Now, we need to declare our spinner:

internal static var spinner: UIActivityIndicatorView?

We should also declare spinner style. As you may know, Apple Activity Indicator has 3 default styles: white, whiteLarge and gray. Thanks to this open variable, we will be able to use all of them:

open static var style: UIActivityIndicatorViewStyle = .whiteLarge

Beside of this, we need to declare also default background color, and default color of our spinner:

open static var baseBackColor = UIColor(white: 0, alpha: 0.6)
open static var baseColor = UIColor.red

Our next goal is to create spinner function to start spinner:

open static func start(style: UIActivityIndicatorViewStyle = style, backColor: UIColor = baseBackColor, baseColor: UIColor = baseColor) {
    if spinner == nil, let window = UIApplication.shared.keyWindow {
        let frame = UIScreen.main.bounds
        spinner = UIActivityIndicatorView(frame: frame)
        spinner!.backgroundColor = backColor
        spinner!.activityIndicatorViewStyle = style
        spinner?.color = baseColor
        window.addSubview(spinner!)
        spinner!.startAnimating()
    }
}

Ok, we have start function that sets style, background color and base spinner color. What's next? Of course, stop function. It should not only stop animation, but also dismiss spinner.

open static func stop() {
    if spinner != nil {
        spinner!.stopAnimating()
        spinner!.removeFromSuperview()
        spinner = nil
    }
}

Now, if you want to implement Spinner, just go to the View Controller. Then, in place where indicator should start animating (i.e. viewDidLoad or viewWillAppear) just put Spinner.start(). Or, if you want personalize style, backColor and/or baseColor:

Spinner.start(style: .white, backColor: UIColor.white, baseColor: UIColor.green)

Then, in a place where processing is done, simply put: Spinner.stop(). Notice that our indicator is covering not only base view, but also Navigation Bar or TabBar.

But there's one little problem. If your app has not only portrait, but also landscape mode ... Well, we'll need to do more work. Why? Just check the pictures below:



This situation happens because our Spinner doesn't know that device's orientation did change. There are a few ways to fix it. For example, we can use viewWillTransition function in ViewController. Or handle device's orientation from Spinner. But first we need to add a simple function to our Spinner class:

open static func update() {
    if spinner != nil {
        stop()
        start()
    }
}

This func will check if indicator is running (or not), then launch it (or not). The next thing is to implement the function. We can do it in our ViewController:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    Spinner.update()
}

The second way is also very simple. Let's go back to Spinner model, and check function called 'start()'. We can easily add observer to the code:

NotificationCenter.default.addObserver(self, selector: #selector(update), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

So now function start() will be checking if device's orientation has chagned. If yes - start() will run update() function. This is easy! Of course, if we want to use argument #selector, first we need to add @objc to the static function update:

@objc open static func update()

Now reusable spinner should work perfectly. No matter if an app is in landscape or portrait mode.

That’s all, folks :) All code you can get from our Git repository. Enjoy!

Update: Reddit user rohlmann82 suggested auto-layout support and the ability to put it on more than just a UIWindow import UIKit. Rohlmann82 allowed me on publishing his code here in this article.
Here you can find it
. Thanks very much! Great job!

Credits: photo by Jeff Golenski from Unsplash

Share on
comments powered by Disqus