[swift] How to display an activity indicator with text on iOS 8 with Swift?

I wanna show, programmatically, an activity indicator with text, like the one in the Photos app (after editing and saving a picture). How can I do this?

enter image description here

This question is related to swift ios8

The answer is


Xcode 10.1 • Swift 4.2

import UIKit

class ProgressHUD: UIVisualEffectView {

    var title: String?
    var theme: UIBlurEffect.Style = .light

    let strLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 160, height: 46))
    let activityIndicator = UIActivityIndicatorView()

    init(title: String, theme: UIBlurEffect.Style = .light) {
        super.init(effect: UIBlurEffect(style: theme))

        self.title = title
        self.theme = theme

        [activityIndicator, strLabel].forEach(contentView.addSubview(_:))
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func didMoveToSuperview() {
        super.didMoveToSuperview()

        if let superview = self.superview {
            frame = CGRect(x: superview.frame.midX - strLabel.frame.width / 2,
                           y: superview.frame.midY - strLabel.frame.height / 2, width: 160, height: 46)

            layer.cornerRadius = 15.0
            layer.masksToBounds = true

            activityIndicator.frame = CGRect(x: 0, y: 0, width: 46, height: 46)
            activityIndicator.startAnimating()

            strLabel.text = title
            strLabel.font = .systemFont(ofSize: 14, weight: UIFont.Weight.medium)

            switch theme {
            case .dark:
                strLabel.textColor = .white
                activityIndicator.style = .white
            default:
                strLabel.textColor = .gray
                activityIndicator.style = .gray
            }
        }

    }

    func show() {
        self.isHidden = false
    }

    func hide() {
        self.isHidden = true
    }
}

Use:

let progress = ProgressHUD(title: "Authorization", theme: .dark)
[progress].forEach(view.addSubview(_:))

For Swift 5

Indicator with label inside WKWebview

var strLabel = UILabel()
   let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
    let loadingTextLabel = UILabel()
   
    @IBOutlet var indicator: UIActivityIndicatorView!
    @IBOutlet var webView: WKWebView!
    
    var refController:UIRefreshControl = UIRefreshControl()
    
    
    override func viewDidLoad() {
        webView = WKWebView(frame: CGRect.zero)
        webView.navigationDelegate = self
        webView.uiDelegate = self as? WKUIDelegate
        
        let preferences = WKPreferences()
        preferences.javaScriptEnabled = true
        let configuration = WKWebViewConfiguration()
        configuration.preferences = preferences
       
        
        webView.allowsBackForwardNavigationGestures = true
        
        webView.load(URLRequest(url: URL(string: "https://www.google.com")!))
        setBackground()
        
    }
    

    
    func setBackground() {
        view.addSubview(webView)
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        webView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        webView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    }
    
    func showActivityIndicator(show: Bool) {
        if show {
            
            
            strLabel = UILabel(frame: CGRect(x: 55, y: 0, width: 400, height: 66))
            strLabel.text = "Please Wait. Checking Internet Connection..."
            strLabel.font = UIFont(name: "Avenir Light", size: 12)
            
           
            strLabel.textColor = UIColor(white: 0.9, alpha: 0.7)
           
            effectView.frame = CGRect(x: view.frame.midX - strLabel.frame.width/2, y: view.frame.midY - strLabel.frame.height/2 , width: 300, height: 66)
            effectView.layer.cornerRadius = 15
            effectView.layer.masksToBounds = true
            indicator = UIActivityIndicatorView(style: .white)
            indicator.frame = CGRect(x: 0, y: 0, width: 66, height: 66)
            indicator.startAnimating()
            effectView.contentView.addSubview(indicator)
            effectView.contentView.addSubview(strLabel)
            indicator.transform = CGAffineTransform(scaleX: 1.4, y: 1.4);
            effectView.center = webView.center
            view.addSubview(effectView)
            
            
        } else {
            strLabel.removeFromSuperview()
             effectView.removeFromSuperview()
            indicator.removeFromSuperview()
            indicator.stopAnimating()
        }
    }

Here is my screenshort


import UIKit

class ViewControllerUtils {

    let containerView: UIView = {

        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = UIColor(white: 0, alpha: 0.3)
        return view
    }()

    let loadingView: UIView = {

        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = UIColor(white: 0, alpha: 0.7)
        view.clipsToBounds = true
        view.layer.cornerRadius = 10
        return view
    }()

    let activityIndicatorView: UIActivityIndicatorView = {

        let aiv = UIActivityIndicatorView()
        aiv.translatesAutoresizingMaskIntoConstraints = false
        aiv.style = UIActivityIndicatorView.Style.whiteLarge
        return aiv
    }()

    let loadingLabel: UILabel = {

        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "Loading..."
        label.textAlignment = .center
        label.textColor = .white
        label.font = .systemFont(ofSize: 15, weight: UIFont.Weight.medium)
        return label
    }()

    func showLoader() {

        guard let window = UIApplication.shared.keyWindow else { return }

        window.addSubview(containerView)
        containerView.addSubview(loadingView)
        loadingView.addSubview(activityIndicatorView)
        loadingView.addSubview(loadingLabel)

        containerView.leftAnchor.constraint(equalTo: window.leftAnchor).isActive = true
        containerView.rightAnchor.constraint(equalTo: window.rightAnchor).isActive = true
        containerView.topAnchor.constraint(equalTo: window.topAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo: window.bottomAnchor).isActive = true

        loadingView.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
        loadingView.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true
        loadingView.widthAnchor.constraint(equalToConstant: 120).isActive = true
        loadingView.heightAnchor.constraint(equalToConstant: 120).isActive = true

        activityIndicatorView.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
        activityIndicatorView.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true
        activityIndicatorView.widthAnchor.constraint(equalToConstant: 60).isActive = true
        activityIndicatorView.heightAnchor.constraint(equalToConstant: 60).isActive = true

        loadingLabel.leftAnchor.constraint(equalTo: loadingView.leftAnchor).isActive = true
        loadingLabel.rightAnchor.constraint(equalTo: loadingView.rightAnchor).isActive = true
        loadingLabel.bottomAnchor.constraint(equalTo: loadingView.bottomAnchor).isActive = true
        loadingLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true

        DispatchQueue.main.async {

            self.activityIndicatorView.startAnimating()
        }
    }

    func hideLoader() {

        DispatchQueue.main.async {

            self.activityIndicatorView.stopAnimating()
            self.activityIndicatorView.removeFromSuperview()
            self.loadingLabel.removeFromSuperview()
            self.loadingView.removeFromSuperview()
            self.containerView.removeFromSuperview()
        }
    }
}

//// In order to show the activity indicator, call the function from your view controller
// let viewControllerUtils = ViewControllerUtils()
// viewControllerUtils.showLoader()

//// In order to hide the activity indicator, call the function from your view controller
// viewControllerUtils.hideLoader()

class ViewControllerUtils2 {

    var container: UIView = UIView()
    var loadingView: UIView = UIView()
    var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
    let loadingLabel = UILabel()

    func showLoader(_ uiView: UIView) {

        container.frame = uiView.frame
        container.center = uiView.center
        container.backgroundColor = UIColor(white: 0, alpha: 0.3)

        loadingView.frame = CGRect(x: 0, y: 0, width: 120, height: 120)
        loadingView.center = uiView.center
        loadingView.backgroundColor = UIColor(white: 0, alpha: 0.7)
        loadingView.clipsToBounds = true
        loadingView.layer.cornerRadius = 10

        activityIndicator.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
        activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
        activityIndicator.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2)

        loadingLabel.frame = CGRect(x: 0, y: 80, width: 120, height: 40)
        loadingLabel.text = "Loading..."
        loadingLabel.textAlignment = .center
        loadingLabel.textColor = .white
        loadingLabel.font = .systemFont(ofSize: 15, weight: UIFont.Weight.medium)

        uiView.addSubview(container)
        container.addSubview(loadingView)
        loadingView.addSubview(activityIndicator)
        loadingView.addSubview(loadingLabel)

        DispatchQueue.main.async {

            self.activityIndicator.startAnimating()
        }
    }

    func hideLoader() {

        DispatchQueue.main.async {

            self.activityIndicator.stopAnimating()
            self.activityIndicator.removeFromSuperview()
            self.loadingLabel.removeFromSuperview()
            self.loadingView.removeFromSuperview()
            self.container.removeFromSuperview()
        }
    }
}

simple activity controller class !!!

class ActivityIndicator: UIVisualEffectView {


let activityIndictor: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)
let label: UILabel = UILabel()
let blurEffect = UIBlurEffect(style: .Dark)
let vibrancyView: UIVisualEffectView

init() {

    self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect))
    super.init(effect: blurEffect)
    self.setup()
}

required init?(coder aDecoder: NSCoder) {

    self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect))
    super.init(coder: aDecoder)
    self.setup()
}

func setup() {

    contentView.addSubview(vibrancyView)
    vibrancyView.contentView.addSubview(activityIndictor)
    activityIndictor.startAnimating()
}

override func didMoveToSuperview() {
    super.didMoveToSuperview()

    if let superview = self.superview {
        let width: CGFloat = 75.0
        let height: CGFloat = 75.0
        self.frame = CGRectMake(superview.frame.size.width / 2 - width / 2,
                                superview.frame.height / 2 - height / 2,
                                width,
                                height)
        vibrancyView.frame = self.bounds
        let activityIndicatorSize: CGFloat = 40
        activityIndictor.frame = CGRectMake(18, height / 2 - activityIndicatorSize / 2,
                                            activityIndicatorSize,
                                            activityIndicatorSize)
        layer.cornerRadius = 8.0
        layer.masksToBounds = true

    }
}

func show() {
    self.hidden = false
}

func hide() {
    self.hidden = true
}}

usage :-

let activityIndicator = ActivityIndicator()
    self.view.addSubview(activityIndicator)

to hide :-

activityIndicator.hide()

In Swift 3

Declare variables which we will use

var activityIndicator = UIActivityIndicatorView()
let loadingView = UIView()
let loadingLabel = UILabel()

Set label , view and activityIndicator

func setLoadingScreen(myMsg : String) {
    let width: CGFloat = 120
    let height: CGFloat = 30
    let x = (self.view.frame.width / 2) - (width / 2)
    let y = (169 / 2) - (height / 2) + 60
    loadingView.frame = CGRect(x: x, y: y, width: width, height: height)
    self.loadingLabel.textColor = UIColor.white
    self.loadingLabel.textAlignment = NSTextAlignment.center
    self.loadingLabel.text = myMsg
    self.loadingLabel.frame = CGRect(x: 0, y: 0, width: 160, height: 30)
    self.loadingLabel.isHidden = false
    self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.white
    self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    self.activityIndicator.startAnimating()
    loadingView.addSubview(self.spinner)
    loadingView.addSubview(self.loadingLabel)
    self.view.addSubview(loadingView)
}

Start Animation

@IBAction func start_animation(_ sender: Any) {
    setLoadingScreen(myMsg: "Loading...")
}

Stop Animation

@IBAction func stop_animation(_ sender: Any) {
    self.spinner.stopAnimating()
    UIApplication.shared.endIgnoringInteractionEvents()
    self.loadingLabel.isHidden = true
}

For Swift 3

enter image description here

Usage

class LoginTVC: UITableViewController {
    var loadingView : LoadingView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // CASE 1: To Show loadingView on load

        loadingView = LoadingView(uiView: view, message: "Sending you verification code")
    }

    // CASE 2: To show loadingView on click of a button

    @IBAction func showLoadingView(_ sender: UIButton) {
        if let loaderView = loadingView{ // If loadingView already exists
            if loaderView.isHidden() {
                loaderView.show()  // To show activity indicator
            }
        }
        else{
            loadingView = LoadingView(uiView: view, message: "Sending you verification code")
         }
    }
}

    // CASE 3: To hide LoadingView on click of a button

    @IBAction func hideLoadingView(_ sender: UIButton) {
        if let loaderView = loadingView{ // If loadingView already exists 
            self.loadingView.hide()   
        }
    }
}

LoadingView Class

class LoadingView {

    let uiView          :   UIView
    let message         :   String
    let messageLabel    =   UILabel()

    let loadingSV       =   UIStackView()
    let loadingView     =   UIView()
    let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)

    init(uiView: UIView, message: String) {
        self.uiView     =   uiView
        self.message    =   message
        self.setup()
    }

    func setup(){
        let viewWidth   = uiView.bounds.width
        let viewHeight  = uiView.bounds.height

        // Configuring the message label
        messageLabel.text             = message
        messageLabel.textColor        = UIColor.darkGray
        messageLabel.textAlignment    = .center
        messageLabel.numberOfLines    = 3
        messageLabel.lineBreakMode    = .byWordWrapping

        // Creating stackView to center and align Label and Activity Indicator
        loadingSV.axis          = .vertical
        loadingSV.distribution  = .equalSpacing
        loadingSV.alignment     = .center
        loadingSV.addArrangedSubview(activityIndicator)
        loadingSV.addArrangedSubview(messageLabel)

        // Creating loadingView, this acts as a background for label and activityIndicator
        loadingView.frame           = uiView.frame
        loadingView.center          = uiView.center
        loadingView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.3)
        loadingView.clipsToBounds   = true

        // Disabling auto constraints
        loadingSV.translatesAutoresizingMaskIntoConstraints = false

        // Adding subviews
        loadingView.addSubview(loadingSV)
        uiView.addSubview(loadingView)
        activityIndicator.startAnimating()

        // Views dictionary
        let views = [
            "loadingSV": loadingSV
        ]

        // Constraints for loadingSV
        uiView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[loadingSV(300)]-|", options: [], metrics: nil, views: views))
        uiView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(viewHeight / 3)-[loadingSV(50)]-|", options: [], metrics: nil, views: views))
    }

    // Call this method to hide loadingView
    func show() {
        loadingView.isHidden = false
    }

    // Call this method to show loadingView
    func hide(){
        loadingView.isHidden = true
    }

    // Call this method to check if loading view already exists
    func isHidden() -> Bool{
        if loadingView.isHidden == false{
            return false
        }
        else{
            return true
        }
    }
}

Based on "MirekE" answer here is a code that i tested now and its working:

var activityIndicator: UIActivityIndicatorView!

var viewActivityIndicator: UIView!

override func viewDidLoad()
{
    super.viewDidLoad()

    let width: CGFloat = 200.0
    let height: CGFloat = 50.0
    let x = self.view.frame.width/2.0 - width/2.0
    let y = self.view.frame.height/2.0 - height/2.0

    self.viewActivityIndicator = UIView(frame: CGRect(x: x, y: y, width: width, height: height))
    self.viewActivityIndicator.backgroundColor = UIColor(red: 255.0/255.0, green: 204.0/255.0, blue: 51.0/255.0, alpha: 0.5)
    self.viewActivityIndicator.layer.cornerRadius = 10

    self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
    self.activityIndicator.color = UIColor.blackColor()
    self.activityIndicator.hidesWhenStopped = false

    let titleLabel = UILabel(frame: CGRect(x: 60, y: 0, width: 200, height: 50))
    titleLabel.text = "Processing..."

    self.viewActivityIndicator.addSubview(self.activityIndicator)
    self.viewActivityIndicator.addSubview(titleLabel)

    self.view.addSubview(self.viewActivityIndicator)
}

func doSometing()
{
    self.activityIndicator.startAnimating()
    UIApplication.sharedApplication().beginIgnoringInteractionEvents()

    //do something here that will taking time

    self.activityIndicator.stopAnimating()
    UIApplication.sharedApplication().endIgnoringInteractionEvents()
    self.viewActivityIndicator.removeFromSuperview()
}

For activity indicator, its better you create one custom class.

Instead of creating UIActivityIndicator in each UIViewController.Subclass UIView and use from any UIViewController.

Updated for Swift 5.0:

import UIKit
import Foundation
class ProgressIndicator: UIView {

var indicatorColor:UIColor
var loadingViewColor:UIColor
var loadingMessage:String
var messageFrame = UIView()
var activityIndicator = UIActivityIndicatorView()

init(inview:UIView,loadingViewColor:UIColor,indicatorColor:UIColor,msg:String){

    self.indicatorColor = indicatorColor
    self.loadingViewColor = loadingViewColor
    self.loadingMessage = msg
    super.init(frame: CGRect(x: inview.frame.midX - 90, y: inview.frame.midY - 250 , width: 180, height: 50))
    initalizeCustomIndicator()

}
convenience init(inview:UIView) {

    self.init(inview: inview,loadingViewColor: UIColor.brown,indicatorColor:UIColor.black, msg: "Loading..")
}
convenience init(inview:UIView,messsage:String) {

    self.init(inview: inview,loadingViewColor: UIColor.brown,indicatorColor:UIColor.black, msg: messsage)
}

required init?(coder aDecoder: NSCoder) {

    fatalError("init(coder:) has not been implemented")
}

func initalizeCustomIndicator(){

    messageFrame.frame = self.bounds
    activityIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.medium)
    activityIndicator.tintColor = indicatorColor
    activityIndicator.hidesWhenStopped = true
    activityIndicator.frame = CGRect(x: self.bounds.origin.x + 6, y: 0, width: 20, height: 50)
    print(activityIndicator.frame)
    let strLabel = UILabel(frame:CGRect(x: self.bounds.origin.x + 30, y: 0, width: self.bounds.width - (self.bounds.origin.x + 30) , height: 50))
    strLabel.text = loadingMessage
    strLabel.adjustsFontSizeToFitWidth = true
    strLabel.textColor = UIColor.white
    messageFrame.layer.cornerRadius = 15
    messageFrame.backgroundColor = loadingViewColor
    messageFrame.alpha = 0.8
    messageFrame.addSubview(activityIndicator)
    messageFrame.addSubview(strLabel)


}

func  start(){
    //check if view is already there or not..if again started
    if !self.subviews.contains(messageFrame){

        activityIndicator.startAnimating()
        self.addSubview(messageFrame)

    }
}

func stop(){

    if self.subviews.contains(messageFrame){

        activityIndicator.stopAnimating()
        messageFrame.removeFromSuperview()

    }
}
}

Put this class in your project and then call from any ViewController as

var indicator:ProgressIndicator?
override func viewDidLoad() {
    super.viewDidLoad()

    //indicator = ProgressIndicator(inview: self.view,messsage: "Hello from Nepal..")
    //self.view.addSubview(indicator!)
    //OR
    indicator = ProgressIndicator(inview:self.view,loadingViewColor: UIColor.grayColor(), indicatorColor: UIColor.blackColor(), msg: "Landing within minutes,Please hold tight..")
    self.view.addSubview(indicator!)

}

@IBAction func startBtn(sender: AnyObject) {
    indicator!.start()
}


@IBAction func stopBtn(sender: AnyObject) {
    indicator!.stop()
}

You can create your own. For example:

Create a view with white background and rounded corners:

var view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
view.backgroundColor = UIColor.whiteColor()
view.layer.cornerRadius = 10

Add two subviews, a UIActivityIndicatorView and a UILabel:

var wait = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
wait.color = UIColor.blackColor()
wait.hidesWhenStopped = false


var text = UILabel(frame: CGRect(x: 60, y: 0, width: 200, height: 50))
text.text = "Processing..."

view.addSubview(wait)
view.addSubview(text)

While Esq's answer works, I've added my own implementation which is more in line with good component architecture by separating the view into it's own class. It also uses dynamic blurring introduced in iOS 8.

Here is how mine looks with an image background:

enter image description here

The code for this is encapsulated in it's own UIView class which means you can reuse it whenever you desire.

Updated for Swift 3

Usage

func viewDidLoad() {
  super.viewDidLoad()

  // Create and add the view to the screen.
  let progressHUD = ProgressHUD(text: "Saving Photo")
  self.view.addSubview(progressHUD)
  // All done!

  self.view.backgroundColor = UIColor.black
}

UIView Code

import UIKit

class ProgressHUD: UIVisualEffectView {

  var text: String? {
    didSet {
      label.text = text
    }
  }

  let activityIndictor: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
  let label: UILabel = UILabel()
  let blurEffect = UIBlurEffect(style: .light)
  let vibrancyView: UIVisualEffectView

  init(text: String) {
    self.text = text
    self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect))
    super.init(effect: blurEffect)
    self.setup()
  }

  required init?(coder aDecoder: NSCoder) {
    self.text = ""
    self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect))
    super.init(coder: aDecoder)
    self.setup()
  }

  func setup() {
    contentView.addSubview(vibrancyView)
    contentView.addSubview(activityIndictor)
    contentView.addSubview(label)
    activityIndictor.startAnimating()
  }

  override func didMoveToSuperview() {
    super.didMoveToSuperview()

    if let superview = self.superview {

      let width = superview.frame.size.width / 2.3
      let height: CGFloat = 50.0
      self.frame = CGRect(x: superview.frame.size.width / 2 - width / 2,
                      y: superview.frame.height / 2 - height / 2,
                      width: width,
                      height: height)
      vibrancyView.frame = self.bounds

      let activityIndicatorSize: CGFloat = 40
      activityIndictor.frame = CGRect(x: 5,
                                      y: height / 2 - activityIndicatorSize / 2,
                                      width: activityIndicatorSize,
                                      height: activityIndicatorSize)

      layer.cornerRadius = 8.0
      layer.masksToBounds = true
      label.text = text
      label.textAlignment = NSTextAlignment.center
      label.frame = CGRect(x: activityIndicatorSize + 5,
                           y: 0,
                           width: width - activityIndicatorSize - 15,
                           height: height)
      label.textColor = UIColor.gray
      label.font = UIFont.boldSystemFont(ofSize: 16)
    }
  }

  func show() {
    self.isHidden = false
  }

  func hide() {
    self.isHidden = true
  }
}

Swift 2

An example on how to use it is like this:

override func viewDidLoad() {
    super.viewDidLoad()

    // Create and add the view to the screen.
    let progressHUD = ProgressHUD(text: "Saving Photo")
    self.view.addSubview(progressHUD)
    // All done!

    self.view.backgroundColor = UIColor.blackColor()
}

Here is the UIView code:

import UIKit

class ProgressHUD: UIVisualEffectView {

    var text: String? {
        didSet {
            label.text = text
        }
    }
    let activityIndictor: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
    let label: UILabel = UILabel()
    let blurEffect = UIBlurEffect(style: .Light)
    let vibrancyView: UIVisualEffectView

    init(text: String) {
        self.text = text
        self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect))
        super.init(effect: blurEffect)
        self.setup()
    }

    required init(coder aDecoder: NSCoder) {
        self.text = ""
        self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect))
        super.init(coder: aDecoder)
        self.setup()

    }

    func setup() {
        contentView.addSubview(vibrancyView)
        vibrancyView.contentView.addSubview(activityIndictor)
        vibrancyView.contentView.addSubview(label)
        activityIndictor.startAnimating()
    }

    override func didMoveToSuperview() {
        super.didMoveToSuperview()

        if let superview = self.superview {

            let width = superview.frame.size.width / 2.3
            let height: CGFloat = 50.0
            self.frame = CGRectMake(superview.frame.size.width / 2 - width / 2,
                superview.frame.height / 2 - height / 2,
                width,
                height)
            vibrancyView.frame = self.bounds

            let activityIndicatorSize: CGFloat = 40
            activityIndictor.frame = CGRectMake(5, height / 2 - activityIndicatorSize / 2,
                activityIndicatorSize,
                activityIndicatorSize)

            layer.cornerRadius = 8.0
            layer.masksToBounds = true
            label.text = text
            label.textAlignment = NSTextAlignment.Center
            label.frame = CGRectMake(activityIndicatorSize + 5, 0, width - activityIndicatorSize - 15, height)
            label.textColor = UIColor.grayColor()
            label.font = UIFont.boldSystemFontOfSize(16)
        }
    }

    func show() {
        self.hidden = false
    }

    func hide() {
        self.hidden = true
    }
}

I hope this helps, please feel free to use this code wherever you need.


Heres how this code looks:

enter image description here

Heres my drag and drop code:

var boxView = UIView()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    view.backgroundColor = UIColor.blackColor()
    addSavingPhotoView()

    //Custom button to test this app
    var button = UIButton(frame: CGRect(x: 20, y: 20, width: 20, height: 20))
    button.backgroundColor = UIColor.redColor()
    button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)

    view.addSubview(button)
}

func addSavingPhotoView() {
    // You only need to adjust this frame to move it anywhere you want
    boxView = UIView(frame: CGRect(x: view.frame.midX - 90, y: view.frame.midY - 25, width: 180, height: 50))
    boxView.backgroundColor = UIColor.whiteColor()
    boxView.alpha = 0.8
    boxView.layer.cornerRadius = 10

    //Here the spinnier is initialized
    var activityView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
    activityView.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    activityView.startAnimating()

    var textLabel = UILabel(frame: CGRect(x: 60, y: 0, width: 200, height: 50))
    textLabel.textColor = UIColor.grayColor()
    textLabel.text = "Saving Photo"

    boxView.addSubview(activityView)
    boxView.addSubview(textLabel)

    view.addSubview(boxView)
}

func buttonAction(sender:UIButton!) {
    //When button is pressed it removes the boxView from screen
    boxView.removeFromSuperview()
}

Here is an open source version of this: https://github.com/goktugyil/CozyLoadingActivity


With auto width and theme support also detects rotate while busy (Swift 3 version)

Use it like below:

var progressView: ProgressView?
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.progressView = ProgressView(message: "Work in progress!",
                                         theme: .dark,
                                         isModal: true)
}

@IBAction func onPause(_ sender: AnyObject) {
    self.progressView.show()      
}

@IBAction func onResume(_ sender: AnyObject) {
    self.progressView.hide()       
}

ProgressView.swift

import UIKit

class ProgressView: UIView {

    enum Theme {
        case light
        case dark
    }

    var theme: Theme
    var container: UIStackView
    var activityIndicator: UIActivityIndicatorView
    var label: UILabel
    var glass: UIView


    private var message: String
    private var isModal: Bool

    init(message: String, theme: theme, isModal: Bool) {
        // Init
        self.message = message
        self.theme = theme
        self.isModal = isModal

        self.container = UIStackView()
        self.activityIndicator = UIActivityIndicatorView()
        self.label = UILabel()
        self.glass = UIView()

        // Get proper width by text message
        let fontName = self.label.font.fontName
        let fontSize = self.label.font.pointSize
        if let font = UIFont(name: fontName, size: fontSize) {
            let fontAttributes = [NSFontAttributeName: font]
            let size = (message as NSString).size(attributes: fontAttributes)
            super.init(frame: CGRect(x: 0, y: 0, width: size.width + 50, height: 50))
        } else {
            super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
        }

        // Detect rotation
        NotificationCenter.default.addObserver(self, selector: #selector(onRotate), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

        // Style
        self.layer.cornerRadius = 3
        if (self.theme == .dark) {
            self.backgroundColor = .darkGray
        } else {
            self.backgroundColor = .lightGray
        }

        // Label
        if self.theme == .dark {
            self.label.textColor = .white
        }else{
            self.label.textColor = .black
        }
        self.label.text = self.message
        // Container
        self.container.frame = self.frame
        self.container.spacing = 5
        self.container.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        self.container.isLayoutMarginsRelativeArrangement = true
        // Activity indicator
        if (self.theme == .dark) {
            self.activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
            self.activityIndicator.color = .white
        } else {
            self.activityIndicator = UIActivityIndicatorView(activityIndicatorStyle:.whiteLarge)
            self.activityIndicator.color = .black
        }
        self.activityIndicator.startAnimating()
        // Add them to container

        // First glass
        if let superview = UIApplication.shared.keyWindow {
            if (self.isModal) {
                // glass
                self.glass.frame = superview.frame;
                if (self.theme == .dark) {
                    self.glass.backgroundColor = UIColor.black.withAlphaComponent(0.5)
                } else {
                    self.glass.backgroundColor = UIColor.white.withAlphaComponent(0.5)
                }
                superview.addSubview(glass)
            }
        }
        // Then activity indicator and label
        container.addArrangedSubview(self.activityIndicator)
        container.addArrangedSubview(self.label)
        // Last attach it to container (StackView)
        self.addSubview(container)
        if let superview = UIApplication.shared.keyWindow {
            self.center = superview.center
            superview.addSubview(self)
        }
        //Do not show until show() is called
        self.hide()
    }

    required init(coder: NSCoder) {
        self.theme = .dark
        self.Message = "Not set!"
        self.isModal = true
        self.container = UIStackView()
        self.activityIndicator = UIActivityIndicatorView()
        self.label = UILabel()
        self.glass = UIView()
        super.init(coder: coder)!
    }

    func onRotate() {
        if let superview = self.superview {
            self.glass.frame = superview.frame
            self.center = superview.center
//            superview.addSubview(self)
        }
    }

    public func show() {
        self.glass.isHidden = false
        self.isHidden = false
    }

    public func hide() {
        self.glass.isHidden = true
        self.isHidden = true
    }
}

Based o my previous answer, here is a more elegant solution with a custom class:

First define this custom class:

import UIKit
import Foundation

class ActivityIndicatorView
{
    var view: UIView!

var activityIndicator: UIActivityIndicatorView!

var title: String!

init(title: String, center: CGPoint, width: CGFloat = 200.0, height: CGFloat = 50.0)
{
    self.title = title

    let x = center.x - width/2.0
    let y = center.y - height/2.0

    self.view = UIView(frame: CGRect(x: x, y: y, width: width, height: height))
    self.view.backgroundColor = UIColor(red: 255.0/255.0, green: 204.0/255.0, blue: 51.0/255.0, alpha: 0.5)
    self.view.layer.cornerRadius = 10

    self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
    self.activityIndicator.color = UIColor.blackColor()
    self.activityIndicator.hidesWhenStopped = false

    let titleLabel = UILabel(frame: CGRect(x: 60, y: 0, width: 200, height: 50))
    titleLabel.text = title
    titleLabel.textColor = UIColor.blackColor()

    self.view.addSubview(self.activityIndicator)
    self.view.addSubview(titleLabel)
}

func getViewActivityIndicator() -> UIView
{
    return self.view
}

func startAnimating()
{
    self.activityIndicator.startAnimating()
    UIApplication.sharedApplication().beginIgnoringInteractionEvents()
}

func stopAnimating()
{
    self.activityIndicator.stopAnimating()
    UIApplication.sharedApplication().endIgnoringInteractionEvents()

    self.view.removeFromSuperview()
}
//end
}

Now on your UIViewController class:

var activityIndicatorView: ActivityIndicatorView!

override func viewDidLoad()
{
    super.viewDidLoad()

    self.activityIndicatorView = ActivityIndicatorView(title: "Processing...", center: self.view.center)
    self.view.addSubview(self.activityIndicatorView.getViewActivityIndicator())

}

func doSomething()
{
    self.activityIndicatorView.startAnimating()
    UIApplication.sharedApplication().beginIgnoringInteractionEvents()

    //do something here that will taking time

    self.activityIndicatorView.stopAnimating()
}

This code work in SWIFT 2.0.

Must Declare a variable for initialize UIActivityIndicatorView

let actInd: UIActivityIndicatorView = UIActivityIndicatorView() 

After initialize put this code in your controller.

actInd.center = ImageView.center
actInd.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
view.addSubview(actInd)
actInd.startAnimating()

after your download process complete then hide a animation.

self.actInd.stopAnimating()