I'm a noob in Swift and XCode and it may be a silly question, but I can't figure it out on my own :(
This is how my LoginViewController looks like. Image1
And I have the following function that triggers when I press the submit button.
#objc func loginButtontapped(){
if emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
customAlert.showAlert(with: "Error", message: "Fill in the fields.", on: self)
} else {
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
Auth.auth().signIn(withEmail: email, password: password) { (result, err) in
if err != nil {
self.customAlert.showAlert(with: "Error", message: "Invalid email or password.", on: self)
}else{
let homepageVC = HomepageViewController()
homepageVC.modalPresentationStyle = .fullScreen
let transition = CATransition()
transition.duration = 0.3
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromLeft
transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
self.view.window!.layer.add(transition, forKey: kCATransition)
self.present(homepageVC, animated: false, completion: nil)
}}}}
Basically it verifies if the TextFields are empty and after that it shows a view with a message.
The first time after I run the project and I click Submit with the empty fields, I get the desired result, as can be seen here. Screenshot here
The problem appears the second time i press the submit button, after i complete the fields with some text as it shows the views overlapped.
Screenshot here
What I'm trying to do is to show one "alert" at a time, depending on the situation.
Below is how my CustomAlert is build.
class CustomAlert{
struct Constants {
static let backgroundAlphaTo: CGFloat = 0.6
}
private let backgroundView: UIView = {
let backgroundView = UIView()
backgroundView.backgroundColor = .black
backgroundView.alpha = 0
return backgroundView
}()
private let alertView: UIView = {
let alertView = UIView()
alertView.backgroundColor = UIColor(rgb: 0x363636, alphaVal: 1)
alertView.layer.masksToBounds = true
alertView.layer.cornerRadius = 12
return alertView
}()
private var myTargetView: UIView?
func showAlert(with title: String, message: String, on ViewController: UIViewController){
guard let targetView = ViewController.view else{
return
}
myTargetView = targetView
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
targetView.addSubview(alertView)
alertView.frame = CGRect(x: 40,
y: -300,
width: targetView.frame.size.width-80,
height: 180)
let titleLabel = UILabel(frame: CGRect(x: 20,
y: 10,
width: alertView.frame.size.width,
height: 40))
titleLabel.text = title
titleLabel.textAlignment = .left
titleLabel.font = UIFont(name: "Roboto-Regular", size: 20)
titleLabel.textColor = .white
alertView.addSubview(titleLabel)
let messageLabel = UILabel(frame: CGRect(x: 20,
y: 40,
width: alertView.frame.size.width-30,
height: 60))
messageLabel.text = message
messageLabel.font = UIFont(name: "Roboto-Thin", size: 18)
messageLabel.textColor = .white
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .left
alertView.addSubview(messageLabel)
let button = UIButton (frame: CGRect(x: alertView.frame.size.width-130,
y: alertView.frame.size.height-70,
width: (alertView.frame.midX)/2,
height: 50))
button.setTitle("Okay", for: .normal)
button.applyGradient(colors: [Helper.UIColorFromRGB(0x694BCE).cgColor,Helper.UIColorFromRGB(0x7D3297).cgColor])
button.addTarget(self, action: #selector(dismissAlert), for: .touchUpInside)
alertView.addSubview(button)
UIView.animate(withDuration: 0.25, animations: {
self.backgroundView.alpha = Constants.backgroundAlphaTo},
completion: {done in
if done{
UIView.animate(withDuration: 0.25, animations: {
self.alertView.center = targetView.center
})
}
})
}
#objc func dismissAlert(){
guard let targetView = myTargetView else{
return
}
UIView.animate(withDuration: 0.25, animations: {
self.alertView.frame = CGRect(x: 40,
y: targetView.frame.size.height,
width: targetView.frame.size.width-80,
height: 300)
},
completion: { [weak self] done in
if done{
UIView.animate(withDuration: 0.25, animations: {
self?.backgroundView.alpha = 0
}, completion: { done in
if done {
self?.alertView.removeFromSuperview()
self?.backgroundView.removeFromSuperview()
}
})
}
})
}
}
It looks like you're adding a new UILabel each time you call showAlert function. You should add this label as child of alertView and just change it's text.
i managed to fix it by adding in the dismissAlert() func, the following 2 lines of code:
myTitleLabel?.text = ""
myMessageLabel?.text = ""
I also made those 2 global.
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")
}
}
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()
}
}