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?
Xcode 9.0 • Swift 4.0
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var filterButton: UIButton!
#IBOutlet weak var saveButton: UIButton!
let destinationUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
.appendingPathComponent("filteredImage.png")
let imagePicker = UIImagePickerController()
let messageFrame = UIView()
var activityIndicator = UIActivityIndicatorView()
var strLabel = UILabel()
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
func activityIndicator(_ title: String) {
strLabel.removeFromSuperview()
activityIndicator.removeFromSuperview()
effectView.removeFromSuperview()
strLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 160, height: 46))
strLabel.text = title
strLabel.font = .systemFont(ofSize: 14, weight: .medium)
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: 160, height: 46)
effectView.layer.cornerRadius = 15
effectView.layer.masksToBounds = true
activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .white)
activityIndicator.frame = CGRect(x: 0, y: 0, width: 46, height: 46)
activityIndicator.startAnimating()
effectView.contentView.addSubview(activityIndicator)
effectView.contentView.addSubview(strLabel)
view.addSubview(effectView)
}
func saveImage() {
do {
try imageView.image?.data?.write(to: destinationUrl, options: .atomic)
print("file saved")
} catch {
print(error)
}
}
func applyFilterToImage() {
imageView.image = imageView.image?.applying(contrast: 1.5)
}
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/a/a8/VST_images_the_Lagoon_Nebula.jpg"), let data = try? Data(contentsOf: url), let image = UIImage(data: data) else { return }
view.backgroundColor = UIColor(white: 0, alpha: 1)
imageView.image = image
}
#IBAction func startSavingImage(_ sender: AnyObject) {
saveButton.isEnabled = false
filterButton.isEnabled = false
activityIndicator("Saving Image")
DispatchQueue.main.async {
self.saveImage()
DispatchQueue.main.async {
self.effectView.removeFromSuperview()
self.saveButton.isEnabled = true
self.filterButton.isEnabled = true
}
}
}
#IBAction func filterAction(_ sender: AnyObject) {
filterButton.isEnabled = false
saveButton.isEnabled = false
activityIndicator("Applying Filter")
DispatchQueue.main.async {
self.applyFilterToImage()
DispatchQueue.main.async {
self.effectView.removeFromSuperview()
self.filterButton.isEnabled = true
self.saveButton.isEnabled = true
}
}
}
#IBAction func cameraAction(_ sender: AnyObject) {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [AnyHashable: Any]!) {
dismiss(animated: true, completion: nil)
imageView.image = image
}
}
extension Data {
var image: UIImage? { return UIImage(data: self) }
}
extension UIImage {
var data: Data? { return UIImagePNGRepresentation(self) }
func applying(contrast value: NSNumber) -> UIImage? {
guard let ciImage = CIImage(image: self)?.applyingFilter("CIColorControls", withInputParameters: [kCIInputContrastKey: value]) else { return nil }
UIGraphicsBeginImageContextWithOptions(size, false, scale)
defer { UIGraphicsEndImageContext() }
UIImage(ciImage: ciImage).draw(in: CGRect(origin: .zero, size: size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
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:
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
}
}
Heres how this code looks:
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
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()
}
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()
}
For Swift 3
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
}
}
}
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)
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()
}
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()
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(_:))
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
}
}
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()
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
}
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()
}
}
}
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()
}
}
Related
this is the code where i am configuring the cell.
cell.configure(image: models[indexPath.item].image, tagText: models[indexPath.item].tag, priceIcon: models[indexPath.item].priceIcon, value: models[indexPath.item].price)
and this is my code for cell.
//
// ImageCollectionViewCell.swift
// tr0ve-iOSApp
//
//
//
// ImageCollectionViewCell.swift
// MyCollectionView
//
import UIKit
class ImageCollectionViewCell: UICollectionViewCell {
static let identifier = "ImageCollectionViewCell"
let itemView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(itemView)
contentView.clipsToBounds = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
itemView.frame = contentView.bounds
}
override func prepareForReuse() {
super.prepareForReuse()
itemView.subviews.forEach { view in
view.removeFromSuperview()
}
}
func configure (image: UIImage?, tagText: String, priceIcon: UIImage, value: Float){
let imageView = UIImageView(image: image)
let priceTagView = UIView(frame: CGRect(x: 5, y: contentView.frame.size.height-50, width: 60, height: 20))
priceTagView.backgroundColor = .black
let valueLabel = UILabel(frame: CGRect(x: 20, y: 5, width: 35, height: 10))
valueLabel.textColor = .white
valueLabel.text = String(value)
valueLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular)
let priceIconView = UIImageView(frame: CGRect(x: 5, y: 5, width: 10, height: 10))
priceIconView.image = priceIcon
priceTagView.addSubview(priceIconView)
priceTagView.addSubview(valueLabel)
let tag = UILabel(frame: CGRect(x: 100, y: contentView.frame.size.height-50, width: 20, height: 20))
tag.text = tagText
tag.textColor = .white
tag.textAlignment = .center
if tagText == "UC" {
tag.backgroundColor = .green
}
else {
tag.backgroundColor = .blue
}
itemView.addSubview(imageView)
itemView.addSubview(priceTagView)
itemView.addSubview(tag)
}
}
[in this image i want to add subview in cell's bottom, and in tried it using image's frame and by contentView's frame. but it is doing good for some cells and bad for the other ones. and when i scroll everyhing meshes up.1
I want put badge on UIBarButtonItem. for that I use the following reference
Add badge alert in right bar button item in swift
in this I create the 'UIBarButtonItem+Badge.swift' file and put that code in it. In my viewcontroller I take the outlet of the UIBarButtonItem. And call the function but it didn't work for me. my viewcontroller file is this
My UIBarButtonItem+Badge.swift file is
extension CAShapeLayer {
func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).cgPath
}
}
private var handle: UInt8 = 0;
extension UIBarButtonItem {
private var badgeLayer: CAShapeLayer? {
if let b: AnyObject = objc_getAssociatedObject(self, &handle) as AnyObject? {
return b as? CAShapeLayer
} else {
return nil
}
}
func setBadge(text: String?, withOffsetFromTopRight offset: CGPoint = CGPoint.zero, andColor color:UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11)
{
badgeLayer?.removeFromSuperlayer()
if (text == nil || text == "") {
return
}
addBadge(text: text!, withOffset: offset, andColor: color, andFilled: filled)
}
func addBadge(text: String, withOffset offset: CGPoint = CGPoint.zero, andColor color: UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11)
{
guard let view = self.value(forKey: "view") as? UIView else { return }
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) { font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: UIFont.Weight.regular) }
let badgeSize = text.size(withAttributes: [NSAttributedString.Key.font: font])
// Initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height;
var width = badgeSize.width + 2 /* padding */
//make sure we have at least a circle
if (width < height) {
width = height
}
//x position is offset from right-hand side
let x = view.frame.width - width + offset.x
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
// Initialiaze Badge's label
let label = CATextLayer()
label.string = text
label.alignmentMode = CATextLayerAlignmentMode.center
label.font = font
label.fontSize = font.pointSize
label.frame = badgeFrame
label.foregroundColor = filled ? UIColor.white.cgColor : color.cgColor
label.backgroundColor = UIColor.clear.cgColor
label.contentsScale = UIScreen.main.scale
badge.addSublayer(label)
// Save Badge as UIBarButtonItem property
objc_setAssociatedObject(self, &handle, badge, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
private func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
my viewcontroller file is this
import UIKit
#IBOutlet weak var notificationLabel: UIBarButtonItem!
in view didload function
notificationLabel.addBadge(text: "4")
Here is a swift 4 solution of #VishalPethani with small convenient changes.
Add this UIBarButtonItem to you code:
class BadgedButtonItem: UIBarButtonItem {
public func setBadge(with value: Int) {
self.badgeValue = value
}
private var badgeValue: Int? {
didSet {
if let value = badgeValue,
value > 0 {
lblBadge.isHidden = false
lblBadge.text = "\(value)"
} else {
lblBadge.isHidden = true
}
}
}
var tapAction: (() -> Void)?
private let filterBtn = UIButton()
private let lblBadge = UILabel()
override init() {
super.init()
setup()
}
init(with image: UIImage?) {
super.init()
setup(image: image)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup(image: UIImage? = nil) {
self.filterBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
self.filterBtn.adjustsImageWhenHighlighted = false
self.filterBtn.setImage(image, for: .normal)
self.filterBtn.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
self.lblBadge.frame = CGRect(x: 20, y: 0, width: 15, height: 15)
self.lblBadge.backgroundColor = .red
self.lblBadge.clipsToBounds = true
self.lblBadge.layer.cornerRadius = 7
self.lblBadge.textColor = UIColor.white
self.lblBadge.font = UIFont.systemFont(ofSize: 10)
self.lblBadge.textAlignment = .center
self.lblBadge.isHidden = true
self.lblBadge.minimumScaleFactor = 0.1
self.lblBadge.adjustsFontSizeToFitWidth = true
self.filterBtn.addSubview(lblBadge)
self.customView = filterBtn
}
#objc func buttonPressed() {
if let action = tapAction {
action()
}
}
}
And then you can use it like that:
class ViewController: UIViewController {
let btn = BadgedButtonItem(with: UIImage(named: "your_image"))
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = btn
btn.tapAction = {
self.btn.setBadge(with: 1)
}
}
}
Here is a repository for that with some customisation
https://github.com/Syngmaster/BadgedBarButtonItem
Here it is a simple solution for putting the badge on a navigation bar
let filterBtn = UIButton.init(frame: CGRect.init(x: 0, y: 0, width: 30, height: 30))
filterBtn.setImage(UIImage.fontAwesomeIcon(name: .filter, style: .solid,
textColor: UIColor.white,
size: CGSize(width: 25, height: 25)), for: .normal)
filterBtn.addTarget(self, action: #selector(filterTapped), for: .touchUpInside)
let lblBadge = UILabel.init(frame: CGRect.init(x: 20, y: 0, width: 15, height: 15))
self.lblBadge.backgroundColor = COLOR_GREEN
self.lblBadge.clipsToBounds = true
self.lblBadge.layer.cornerRadius = 7
self.lblBadge.textColor = UIColor.white
self.lblBadge.font = FontLatoRegular(s: 10)
self.lblBadge.textAlignment = .center
filterBtn.addSubview(self.lblBadge)
self.navigationItem.rightBarButtonItems = [UIBarButtonItem.init(customView: filterBtn)]
In your case
self.navigationItem.rightBarButtonItems = [notificationLabel.init(customView: filterBtn)]
import Foundation
import UIKit
extension UIBarButtonItem {
convenience init(icon: UIImage, badge: String, _ badgeBackgroundColor: UIColor = #colorLiteral(red: 0.9156965613, green: 0.380413115, blue: 0.2803866267, alpha: 1), target: Any? = self, action: Selector? = nil) {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
imageView.image = icon
let label = UILabel(frame: CGRect(x: -8, y: -5, width: 18, height: 18))
label.text = badge
label.backgroundColor = badgeBackgroundColor
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 10)
label.clipsToBounds = true
label.layer.cornerRadius = 18 / 2
label.textColor = .white
let buttonView = UIView(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
buttonView.addSubview(imageView)
buttonView.addSubview(label)
buttonView.addGestureRecognizer(UITapGestureRecognizer.init(target: target, action: action))
self.init(customView: buttonView)
}
}
Use:
item = UIBarButtonItem(icon: UIImage(), badge: "\(Test)", target: self, action: nil)
self.navigationItem.rightBarButtonItems = [item]
extension UIBarButtonItem {
func setBadge(with value: Int) {
guard let lblBadge = customView?.viewWithTag(100) as? UILabel else { return }
if value > 0 {
lblBadge.isHidden = false
lblBadge.text = "\(value)"
} else {
lblBadge.isHidden = true
}
}
func setup(image: UIImage? = nil) {
customView?.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let lblBadge = UILabel()
lblBadge.frame = CGRect(x: 20, y: 0, width: 15, height: 15)
lblBadge.backgroundColor = .red
lblBadge.tag = 100
lblBadge.clipsToBounds = true
lblBadge.layer.cornerRadius = 7
lblBadge.textColor = UIColor.white
lblBadge.font = UIFont.systemFont(ofSize: 10)
lblBadge.textAlignment = .center
lblBadge.isHidden = true
lblBadge.minimumScaleFactor = 0.1
lblBadge.adjustsFontSizeToFitWidth = true
customView?.addSubview(lblBadge)
}
}
Steps:
Drag an drop an UIButton in navigation/toolbar or add it programmatically using customView initializer.
Call setup method in view didload:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
itemsButton.setup(image: UIImage(named: "image_name"))
}
Connect an #IBOutlet/or programmatically and call setBadge method everywhere you need:
badgeButton.setBadge(with: 10)
I know this question has been asked lots of times but none of solutions worked for me.
I have a custom UIView class which I use for displaying alert message. I added UIButton to close the view. However, nothing happens when I tab it.
import UIKit
public class Alert: UIView {
public var image: UIImage?
public var title: String?
public var message: String?
public var closeButtonText: String?
public var dialogBackgroundColor: UIColor = .white
public var dialogTitleTextColor: UIColor = .black
public var dialogMessageTextColor: UIColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
public var dialogImageColor: UIColor = UIColor(red:0.47, green:0.72, blue:0.35, alpha:1.0)
public var overlayColor: UIColor = .black
public var overlayOpacity: CGFloat = 0.66
public var paddingSingleTextOnly: CGFloat = 8
public var paddingTopAndBottom: CGFloat = 24
public var paddingFromSides: CGFloat = 8
public var seperatorHeight: CGFloat = 6
private var height: CGFloat = 0
private var width: CGFloat = 0
private var maxSize: CGSize = CGSize()
private let marginFromSides: CGFloat = 80
public lazy var imageSize: CGSize = CGSize(width: 75, height: 75)
public var overlay = false
public var blurOverlay = true
//animation duration
public var duration = 0.33
private var onComplete: (() -> Void)?
#objc public var titleFont: UIFont = UIFont.systemFont(ofSize: 18)
#objc public var messageFont: UIFont = UIFont.systemFont(ofSize: 15)
private lazy var backgroundView: UIView = {
let view = UIView()
view.alpha = 0
return view
}()
public let dialogView: UIView = {
let view = UIView()
view.layer.cornerRadius = 6
view.layer.masksToBounds = true
view.alpha = 0
view.clipsToBounds = true
return view
}()
private lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
return view
}()
public lazy var closeButton: UIButton = {
let button = UIButton()
return button
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
#objc func closeButtonTapped(sender: UIButton){
dismiss()
}
private func calculations() {
height += paddingTopAndBottom
maxSize = CGSize(width: frame.width - marginFromSides * 2, height: frame.height - marginFromSides)
}
public convenience init(title:String, message: String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
self.image = image
}
public convenience init(title:String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.image = image
}
public convenience init(title: String, message: String) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
}
public convenience init(message: String) {
self.init(frame: UIScreen.main.bounds)
paddingTopAndBottom = paddingSingleTextOnly
paddingFromSides = paddingSingleTextOnly * 2
self.message = message
}
override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createOverlay() {
backgroundView.frame = frame
backgroundView.backgroundColor = overlayColor
backgroundView.isUserInteractionEnabled = true
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createBlurOverlay() {
backgroundView.frame = frame
//Blur Effect
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = frame
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundView.addSubview(blurEffectView)
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createTitle(title: String) {
titleLabel.font = titleFont
titleLabel.text = title
titleLabel.frame.origin.y = height + 2
let titleLabelSize = titleLabel.sizeThatFits(maxSize)
handleSize(size: titleLabelSize)
titleLabel.frame.size = titleLabelSize
titleLabel.textColor = self.dialogTitleTextColor
dialogView.addSubview(titleLabel)
}
private func createMessage(message: String) {
messageLabel.font = messageFont
messageLabel.text = message
messageLabel.frame.origin.y = height
let messageLabelSize = messageLabel.sizeThatFits(maxSize)
messageLabel.frame.size = messageLabelSize
messageLabel.textColor = self.dialogMessageTextColor
handleSize(size: messageLabelSize)
dialogView.addSubview(messageLabel)
}
private func createImage(image: UIImage) {
imageView.image = image.withRenderingMode(.alwaysTemplate)
imageView.frame.origin.y = height
imageView.frame.size = imageSize
imageView.tintColor = self.dialogImageColor
handleSize(size: imageSize)
dialogView.addSubview(imageView)
}
private func createButton(){
closeButton.setTitle("Close", for: .normal)
closeButton.tintColor = UIColor.white
closeButton.frame.origin.y = height + 20
let closeButtonSize = CGSize(width: width - 60, height: 60)
closeButton.frame.size = closeButtonSize
closeButton.layer.cornerRadius = 6
closeButton.backgroundColor = Color.NavigationBar.tintColor
closeButton.isUserInteractionEnabled = true
handleSize(size: closeButtonSize)
dialogView.addSubview(closeButton)
}
private func createDialog() {
centerAll()
height += paddingTopAndBottom
dialogView.frame.size = CGSize(width: width, height: height)
dialogView.backgroundColor = self.dialogBackgroundColor
dialogView.isUserInteractionEnabled = true
addSubview(dialogView)
self.dialogView.center = self.center
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
if let window = UIApplication.shared.keyWindow {
window.addSubview(dialogView)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
UIApplication.topViewController()?.view.addSubview(self)
window.view.addSubview(self)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
}
}
private func centerAll() {
if ((messageLabel.text) != nil) {
messageLabel.frame.origin.x = (width - messageLabel.frame.width) / 2
}
if ((titleLabel.text) != nil) {
titleLabel.frame.origin.x = (width - titleLabel.frame.width) / 2
}
if ((imageView.image) != nil) {
imageView.frame.origin.x = (width - imageView.frame.width) / 2
}
closeButton.frame.origin.x = (width - closeButton.frame.width) / 2
}
private func handleSize(size: CGSize) {
if width < size.width + paddingFromSides * 2 {
width = size.width + paddingFromSides * 2
}
if paddingTopAndBottom != paddingSingleTextOnly {
height += seperatorHeight
}
height += size.height
}
private func showAnimation() {
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = self.overlayOpacity
self.dialogView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
self.dialogView.alpha = 1
})
}
public func show() {
if let complete = onComplete {
self.onComplete = complete
}
calculations()
if self.overlay {
if blurOverlay {
createBlurOverlay()
} else {
createOverlay()
}
}
if let img = image {
createImage(image: img)
}
if let title = title {
createTitle(title: title)
}
if let message = message {
createMessage(message: message)
}
createButton()
createDialog()
showAnimation()
}
public func dismiss(){
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = 0
}
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
self.dialogView.alpha = 0
}, completion: { (completed) in
self.dialogView.removeFromSuperview()
if (self.overlay)
{
self.backgroundView.removeFromSuperview()
}
self.removeFromSuperview()
if let completionHandler = self.onComplete {
completionHandler()
}
})
}
}
How I create the alert;
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
If I declare target inside UIViewController (Where I create this UIView) as
Alert.closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:), for: .touchUPInside)
and create function inside UIViewController It is working. I can't figure out why It doesn't work when in custom class.
So my question is that how can close the alert view when tabbed the button?
I tried below solution but didn't work for me;
UIButton target action inside custom class
Assuming these lines are inside a function - such as due to a button tap:
#IBAction func didTap(_ sender: Any) {
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
}
You are creating an instance of your Alert class, calling the .show() function inside it, and then it goes out of scope.
So, as soon as that function exists, alert no longer exists, and no code inside it can run.
You need to have a class-level variable to hold onto it while it is displayed:
class MyViewController: UIViewController {
var alert: Alert?
#IBAction func didTap(_ sender: Any) {
alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert?.show()
}
}
Here is a demonstration of the "Wrong Way" and the "Right Way" to handle your Alert view: https://github.com/DonMag/EmreTest
Look this code
1- Call IBAction inside UIView Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
self.view.addSubview(alert)
}
}
2- Call IBAction inside UIViewController Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
// closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
alert.closeButton.addTarget(self, action: #selector(self.closeButtonTappedController(_:)), for: .touchUpInside)
self.view.addSubview(alert)
}
#IBAction func closeButtonTappedController(_ sender:UIButton){
print("Call 2")
}
}
Hello I have created a swift file in which I wrote some utility functions which I used in multiple controllers. I also wrote function for UIActivityIndicator. But somehow its not working as expected.
Here is my function
static func showIndicatorView(backgroundView: UIView,controller: UIViewController)->UIActivityIndicatorView{
let loadingIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50))
let backgroundView = UIView()
backgroundView.layer.cornerRadius = 05
backgroundView.clipsToBounds = true
backgroundView.opaque = false
backgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.color = UIColor.whiteColor()
loadingIndicator.startAnimating()
let loadingLabel = UILabel()
loadingLabel.text = "Loading..."
loadingLabel.textColor = UIColor.whiteColor()
let textSize: CGSize = loadingLabel.text!.sizeWithAttributes([NSFontAttributeName: loadingLabel.font ])
loadingLabel.frame = CGRectMake(50, 0, textSize.width, textSize.height)
loadingLabel.center.y = loadingIndicator.center.y
backgroundView.frame = CGRectMake(0, 0, textSize.width + 70, 50)
backgroundView.center = controller.view.center;
controller.view.addSubview(backgroundView)
backgroundView.addSubview(loadingIndicator)
backgroundView.addSubview(loadingLabel)
return loadingIndicator
}
I am doing this in controllers in order to show and hide the indicator
show
Utility.showIndicatorView(backgroundView, controller: self).startAnimating()
hide
Utility.showIndicatorView(backgroundView, controller: self).startAnimating()
Sometimes UiIndicatorView background doesn't remove from the controller. Please check my code and let me know how can I show and hide the uiindicator in one or two lines
Create a class for your loading view and add functions to show and hide the view like this:
import UIKit
class LoadingView: UIView {
override init (frame : CGRect) {
super.init(frame : frame)
let loadingIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50))
let backgroundView = UIView()
backgroundView.layer.cornerRadius = 05
backgroundView.clipsToBounds = true
backgroundView.opaque = false
backgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.color = UIColor.whiteColor()
loadingIndicator.startAnimating()
let loadingLabel = UILabel()
loadingLabel.text = "Loading..."
loadingLabel.textColor = UIColor.whiteColor()
let textSize: CGSize = loadingLabel.text!.sizeWithAttributes([NSFontAttributeName: loadingLabel.font ])
loadingLabel.frame = CGRectMake(50, 0, textSize.width, textSize.height)
loadingLabel.center.y = loadingIndicator.center.y
backgroundView.frame = CGRectMake(0, 0, textSize.width + 70, 50)
backgroundView.center = self.center;
self.addSubview(backgroundView)
backgroundView.addSubview(loadingIndicator)
backgroundView.addSubview(loadingLabel)
}
convenience init () {
self.init(frame:UIScreen.mainScreen().bounds)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showLoadingView() {
if let rootViewController = UIApplication.topViewController() {
rootViewController.view.addSubview(self)
self.bringSubviewToFront(rootViewController.view)
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
}
func hideLoadingView() {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
self.removeFromSuperview()
}
}
// Get the visible ViewController
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController where top.view.window != nil {
return topViewController(top)
} else if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
}
Then init your loading view in your ViewController.
let lv = LoadingView()
lv.showLoadingView()
lv.hideLoadingView()
What is example for loading overlay in Swift IOS application when do a long tasks. Example for loading data from remote server.
I googled but not found any answer.
Updated:
Thanks for #Sebastian Dressler this is simple way. I updated my code and it run cool
public class LoadingOverlay{
var overlayView = UIView()
var activityIndicator = UIActivityIndicatorView()
class var shared: LoadingOverlay {
struct Static {
static let instance: LoadingOverlay = LoadingOverlay()
}
return Static.instance
}
public func showOverlay(view: UIView) {
overlayView.frame = CGRectMake(0, 0, 80, 80)
overlayView.center = view.center
overlayView.backgroundColor = UIColor(hex: 0x444444, alpha: 0.7)
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = 10
activityIndicator.frame = CGRectMake(0, 0, 40, 40)
activityIndicator.activityIndicatorViewStyle = .WhiteLarge
activityIndicator.center = CGPointMake(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
overlayView.addSubview(activityIndicator)
view.addSubview(overlayView)
activityIndicator.startAnimating()
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
overlayView.removeFromSuperview()
}
}
let using:
LoadingOverlay.shared.showOverlay(self.view)
//To to long tasks
LoadingOverlay.shared.hideOverlayView()
The above answers add a loading view but it doesn't block click events on the screen also it does not provides overlay for rest of screen. You can achieve it as follows:
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .Alert)
alert.view.tintColor = UIColor.blackColor()
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50)) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
presentViewController(alert, animated: true, completion: nil)
Swift 3.0
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
Swift 4.0 and newer
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
and you can hide it as follows:
dismiss(animated: false, completion: nil)
It will be shown as follows:
Just create yourself an overlay view, which you add to your parent view and remove it once your task is done, e.g. to add it:
var overlay : UIView? // This should be a class variable
[ ... ]
overlay = UIView(frame: view.frame)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
For removal:
overlay?.removeFromSuperview()
Blur background + Activity Indicator, Swift 5 example
extension UIView {
func showBlurLoader() {
let blurLoader = BlurLoader(frame: frame)
self.addSubview(blurLoader)
}
func removeBluerLoader() {
if let blurLoader = subviews.first(where: { $0 is BlurLoader }) {
blurLoader.removeFromSuperview()
}
}
}
class BlurLoader: UIView {
var blurEffectView: UIVisualEffectView?
override init(frame: CGRect) {
let blurEffect = UIBlurEffect(style: .dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = frame
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.blurEffectView = blurEffectView
super.init(frame: frame)
addSubview(blurEffectView)
addLoader()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addLoader() {
guard let blurEffectView = blurEffectView else { return }
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
blurEffectView.contentView.addSubview(activityIndicator)
activityIndicator.center = blurEffectView.contentView.center
activityIndicator.startAnimating()
}
}
For anyone late like me, I made some modifications to #Sonrobby code. As i understand, #Sonrobby adds the activity to the overlay on every showOverlay call. And some of the configuration can be passed to the init function, letting only the placement on the showOverlay method.
I also change the overlay's background to black, since my app it is mostly white.
here is the code :
public class LoadingOverlay{
var overlayView : UIView!
var activityIndicator : UIActivityIndicatorView!
class var shared: LoadingOverlay {
struct Static {
static let instance: LoadingOverlay = LoadingOverlay()
}
return Static.instance
}
init(){
self.overlayView = UIView()
self.activityIndicator = UIActivityIndicatorView()
overlayView.frame = CGRectMake(0, 0, 80, 80)
overlayView.backgroundColor = UIColor(white: 0, alpha: 0.7)
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = 10
overlayView.layer.zPosition = 1
activityIndicator.frame = CGRectMake(0, 0, 40, 40)
activityIndicator.center = CGPointMake(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
activityIndicator.activityIndicatorViewStyle = .WhiteLarge
overlayView.addSubview(activityIndicator)
}
public func showOverlay(view: UIView) {
overlayView.center = view.center
view.addSubview(overlayView)
activityIndicator.startAnimating()
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
overlayView.removeFromSuperview()
}
}
#Ajinkya Patil answer as a reference. Swift 4.0 and newer
This is an Extension Solution to use on all viewController without clashing.
Create a LoadingDialog+ViewContoller.swift
import UIKit
struct ProgressDialog {
static var alert = UIAlertController()
static var progressView = UIProgressView()
static var progressPoint : Float = 0{
didSet{
if(progressPoint == 1){
ProgressDialog.alert.dismiss(animated: true, completion: nil)
}
}
}
}
extension UIViewController{
func LoadingStart(){
ProgressDialog.alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
ProgressDialog.alert.view.addSubview(loadingIndicator)
present(ProgressDialog.alert, animated: true, completion: nil)
}
func LoadingStop(){
ProgressDialog.alert.dismiss(animated: true, completion: nil)
}
}
call the function inside ViewController anywhere you like. like so:
self.LoadingStart()
and here's how to stop the loading dialog.
self.LoadingStop()
To add on to the answers given, you might run into issues if you are attempting to run the code sometimes. Personally, there was an occasion where showOverlay was not being properly called (because I was trying to segue into a scene, then immediately call this function during viewDidLoad).
If you run into an issue similar to mine, there is one fix to the code and a change in approach I recommend.
FIX: Place both blocks of code as closures to a dispatch_async call, like so:
dispatch_async(dispatch_get_main_queue(),
{ //code });
APPROACH: When calling your code, do a dispatch_after call onto the main queue to delay the call by a few milliseconds.
The reasoning? You're simply asking the UI to do too much during viewDidLoad.
If this appendix to the solution helped, I'd be glad.
-Joel Long
P.S. Solution worked for XCode v6.3.2
Use ATKit.
Refer:
https://aurvan.github.io/atkit-ios-release/index.html
ATProgressOverlay Class
https://aurvan.github.io/atkit-ios-release/helpbook/Classes/ATProgressOverlay.html
Code:
import ATKit
ATProgressOverlay.sharedInstance.show() // Does not show network activity indicator on status bar.
ATProgressOverlay.sharedInstance.show(isNetworkActivity: true) // Shows network activity indicator on status bar.
Screenshot:
Swift 5
class func showUniversalLoadingView(_ show: Bool, loadingText : String = "") {
let existingView = UIApplication.shared.windows[0].viewWithTag(1200)
if show {
if existingView != nil {
return
}
let loadingView = self.makeLoadingView(withFrame: UIScreen.main.bounds, loadingText: loadingText)
loadingView?.tag = 1200
UIApplication.shared.windows[0].addSubview(loadingView!)
} else {
existingView?.removeFromSuperview()
}
}
class func makeLoadingView(withFrame frame: CGRect, loadingText text: String?) -> UIView? {
let loadingView = UIView(frame: frame)
loadingView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
let activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
//activityIndicator.backgroundColor = UIColor(red:0.16, green:0.17, blue:0.21, alpha:1)
activityIndicator.layer.cornerRadius = 6
activityIndicator.center = loadingView.center
activityIndicator.hidesWhenStopped = true
activityIndicator.style = .white
activityIndicator.startAnimating()
activityIndicator.tag = 100 // 100 for example
loadingView.addSubview(activityIndicator)
if !text!.isEmpty {
let lbl = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
let cpoint = CGPoint(x: activityIndicator.frame.origin.x + activityIndicator.frame.size.width / 2, y: activityIndicator.frame.origin.y + 80)
lbl.center = cpoint
lbl.textColor = UIColor.white
lbl.textAlignment = .center
lbl.text = text
lbl.tag = 1234
loadingView.addSubview(lbl)
}
return loadingView
}
Uses
showUniversalLoadingView(true, loadingText: "Downloading Data.......")
showUniversalLoadingView(true)
Remove loader
showUniversalLoadingView(false)
Updated #sonrobby answer, added a background view and orientation handling via resizing mask... this can be used for simple stuffs
public class LoadingOverlay{
var overlayView = UIView()
var activityIndicator = UIActivityIndicatorView()
var bgView = UIView()
class var shared: LoadingOverlay {
struct Static {
static let instance: LoadingOverlay = LoadingOverlay()
}
return Static.instance
}
public func showOverlay(view: UIView) {
bgView.frame = view.frame
bgView.backgroundColor = UIColor.gray
bgView.addSubview(overlayView)
bgView.autoresizingMask = [.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin,.flexibleHeight, .flexibleWidth]
overlayView.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
overlayView.center = view.center
overlayView.autoresizingMask = [.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin]
overlayView.backgroundColor = UIColor.black
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = 10
activityIndicator.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
activityIndicator.activityIndicatorViewStyle = .whiteLarge
activityIndicator.center = CGPoint(x: overlayView.bounds.width / 2, y: overlayView.bounds.height / 2)
overlayView.addSubview(activityIndicator)
view.addSubview(bgView)
self.activityIndicator.startAnimating()
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
bgView.removeFromSuperview()
}
}
if you add it to keywindow, it can then go over your nav and tab bars also... something like this
LoadingOverlay.shared.showOverlay(view: UIApplication.shared.keyWindow!)
Swift 3.
I used #Lucho's code in his answer below and I changed the overlay background color to clear and added a spinner color.
public class LoadingOverlay {
var overlayView : UIView!
var activityIndicator : UIActivityIndicatorView!
class var shared: LoadingOverlay {
struct Static {
static let instance: LoadingOverlay = LoadingOverlay()
}
return Static.instance
}
init(){
self.overlayView = UIView()
self.activityIndicator = UIActivityIndicatorView()
overlayView.frame = CGRect(0, 0, 80, 80)
overlayView.backgroundColor = .clear
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = 10
overlayView.layer.zPosition = 1
activityIndicator.frame = CGRect(0, 0, 40, 40)
activityIndicator.center = CGPoint(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
activityIndicator.activityIndicatorViewStyle = .whiteLarge
activityIndicator.color = .gray
overlayView.addSubview(activityIndicator)
}
public func showOverlay(view: UIView) {
overlayView.center = view.center
view.addSubview(overlayView)
activityIndicator.startAnimating()
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
overlayView.removeFromSuperview()
}
}
I've created a protocol for presenting your own view controller as an overlay. The usage is very simple:
class ViewController: UIViewController, OverlayHost {
#IBAction func showOverlayButtonPressed() {
showOverlay(type: YourOverlayViewController.self,
fromStoryboardWithName: "Main")
}
}
Result:
Source code: https://github.com/agordeev/OverlayViewController
Related article: https://andreygordeev.com/2017/04/18/overlay-view-controller-protocols-swift/
Xamarin.iOS version:
var alert = UIAlertController.Create(string.Empty, "Please wait...", UIAlertControllerStyle.Alert);
var alertIndicatorView = new UIActivityIndicatorView();
alertIndicatorView.Frame = new CGRect(x: 10, y: 5, width: 50, height: 50);
alertIndicatorView.ActivityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray;
alertIndicatorView.HidesWhenStopped = true;
alertIndicatorView.StartAnimating();
alert.Add(alertIndicatorView);
controller.PresentViewController(alert, true, null);
If there's someone looking for a Lottie implementation for loading view here's a working solution I made using #Shourob Datta solution:
import Foundation
import UIKit
import Lottie
public class LogoLoadingAnimation{
class func showUniversalLoadingView(_ show: Bool, loadingText : String = "") {
let existingView = UIApplication.shared.windows[0].viewWithTag(1200)
if show {
if existingView != nil {
return
}
let loadingView = self.makeLoadingView(withFrame: UIScreen.main.bounds, loadingText: loadingText)
loadingView?.tag = 1200
UIApplication.shared.windows[0].addSubview(loadingView!)
} else {
existingView?.removeFromSuperview()
}
}
class func makeLoadingView(withFrame frame: CGRect, loadingText text: String?) -> UIView? {
let gradienView = GradientBackgroundView(frame: frame)
gradienView.startColor = UIColor(red: 255, green: 255, blue: 255, alpha: 1)
gradienView.endColor = UIColor(red: 238, green: 238, blue: 238, alpha: 1)
gradienView.startColor = UIColor(named: "dark") ?? .blue
gradienView.startColor = UIColor(named: "purpuleGrey") ?? .gray
let loadingAnimationView = AnimationView()
gradienView.addSubview(loadingAnimationView)
loadingAnimationView.translatesAutoresizingMaskIntoConstraints = false
loadingAnimationView.centerXAnchor.constraint(equalTo: gradienView.centerXAnchor).isActive = true
loadingAnimationView.centerYAnchor.constraint(equalTo: gradienView.centerYAnchor).isActive = true
loadingAnimationView.animation = UITraitCollection.current.userInterfaceStyle == .dark ? Animation.named("logoLoadingWhite") : Animation.named("logoLoadingBlue")
loadingAnimationView.backgroundBehavior = .pauseAndRestore
loadingAnimationView.contentMode = .scaleAspectFit
loadingAnimationView.loopMode = .loop
loadingAnimationView.play()
return gradienView
}
}