Activity indicator not showing when executed - ios

I am trying to display an activity indicator when the user hits the login button. If I put the startActivityIndicator() code in viewDidLoad() it shows on the screen exactly as expected. When I execute it as the first step in btnSignIn() it never appears. A little lost, so i'm hoping the Stack guru's can help...
// Here are the variable declarations
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
var loadingView: UIView = UIView()
var viewCenter:CGPoint!
#IBAction func btnSignIn(sender: AnyObject) {
startActivityIndicator()
if validateEmailAddress(txtEmailAddress.text!) == false {
stopActivityIndicator(self.loadingView)
return
}
if validatePassword(txtPassword.text!) == false {
stopActivityIndicator(self.loadingView)
return
}
PFUser.logInWithUsernameInBackground(txtEmailAddress.text!, password:txtPassword.text!) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
// Successful login.
self.txtPassword.resignFirstResponder()
self.txtEmailAddress.resignFirstResponder()
self.getUserInfo()
} else {
self.stopActivityIndicator(self.loadingView)
// The login failed. Display alert.
self.displayAlert("Whoops!", message: "Email or Password are incorrect.")
}
}
}
func startActivityIndicator() {
loadingView.frame = CGRectMake(0, 0, 80, 80)
loadingView.center = viewCenter
print(viewCenter)
loadingView.backgroundColor = UIColorFromRGB("444444", alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
activityIndicator.center = CGPointMake(loadingView.frame.size.width / 2, loadingView.frame.size.height / 2);
view.addSubview(loadingView)
loadingView.addSubview(activityIndicator)
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
activityIndicator.startAnimating()
}
func stopActivityIndicator(uiView: UIView) {
activityIndicator.stopAnimating()
loadingView.removeFromSuperview()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}

Using my delay utility (see here: https://stackoverflow.com/a/24318861/341994), rewrite like this:
#IBAction func btnSignIn(sender: AnyObject) {
startActivityIndicator()
delay(0.1) {
if validateEmailAddress(txtEmailAddress.text!) == false {
// ... everything else goes here ...
}
}
The delay gives the activity indicator a chance to appear and start spinning.

Related

the button works only once

I have a page which contains two text fields, 2 labels and a button. When the button is clicked I am just checking if the fields are not empty and validate them. This works fine. But I can click the button only once. After that nothing will change and I'm not able to press the button or edit the text fields!
This is my code:
#IBAction func loginbtn(_ sender: Any) {
showActivityIndicator(uiView: view)
emailerrormsg.isHidden=true
passworderrormsg.isHidden=true
if emailtf.text == "" && passwordtf.text == "" {
emailerrormsg.text = "Email is required!"
emailerrormsg.isHidden = false
passworderrormsg.text = "Password is required!"
passworderrormsg.isHidden = false
stopActivityIndicator()
}
else if emailtf.text == ""{
emailerrormsg.text = "Email is required!"
emailerrormsg.isHidden = false
stopActivityIndicator()
}
else if !(isValidEmail(testStr: emailtf.text!)){
emailerrormsg.text = "Please enter a valid email!"
emailerrormsg.isHidden = false
stopActivityIndicator()
}else if passwordtf.text == "" {
passworderrormsg.text = "Password is required!"
passworderrormsg.isHidden = false
stopActivityIndicator()
}
else{
hashedpass = (passwordtf.text!).sha1()
self.login(email: emailtf.text!, password: hashedpass)
}
}
What am I doing wrong?
func stopActivityIndicator(){
DispatchQueue.main.async {
self.loadingView.isHidden = true
}
}
func showActivityIndicator(uiView: UIView) {
let container: UIView = UIView()
container.frame = uiView.frame
container.center = uiView.center
loadingView.frame = CGRect(x: 0,y: 0,width: 80,height: 80)
loadingView.center = uiView.center
loadingView.backgroundColor = UIColor(red:0.16, green:0.27, blue:0.60, alpha:1.0)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
let actInd: UIActivityIndicatorView = UIActivityIndicatorView()
actInd.frame = CGRect(x: 0.0,y: 0.0,width: 40.0,height: 40.0);
actInd.activityIndicatorViewStyle =
UIActivityIndicatorViewStyle.whiteLarge
actInd.center = CGPoint(x: loadingView.frame.size.width / 2,y: loadingView.frame.size.height / 2);
loadingView.addSubview(actInd)
container.addSubview(loadingView)
uiView.addSubview(container)
actInd.startAnimating()
}
You are hiding loading view in the stop method. Instead, hide container view. For this, you would need to make container view a class level object(just as you have made loading view)

Reconnect to the internet automatically and disappear label

Now I want to implement the detect network from the beginning into the app will detect whether the link network,assuming that the user cuts off the network on the way, the screen will display a label and imageView, if the network is reconnected, you can automatically display the information and let label and imageView disappear, but now I do not know how to automatically detect Network, and label and imageView how to put the center of the screen (not using storyboard), thank you!
and here is my code:
func laodingTableviewData() {
self.tableView.isHidden = true
let activityIndicatorView = NVActivityIndicatorView(frame:CGRect(x:ActivityConstraint.Activity.offsetX, y:ActivityConstraint.Activity.offsetY , width:80.0, height:80.0), type: .ballSpinFadeLoader, color: ActivityConstraint.Activity.color, padding: 20)
self.view.addSubview(activityIndicatorView)
activityIndicatorView.startAnimating()
//detect network
guard Reachability.isConnectedToNetwork() == true else {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.5) {
self.failNetworkAlert()
self.failNetworkOutlet()
activityIndicatorView.stopAnimating()
}
return
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.5) {
self.bulletinBoards = BulletinBoard.downloadAllBulletinBoard()
self.tableView.isHidden = false
self.tableView.reloadData()
activityIndicatorView.stopAnimating()
}
}
Create label and imageView:
func failNetworkOutlet() {
let w = self.view.frame.width/2
let h = self.view.frame.height/2
let label = UILabel(frame: CGRect(x: -30, y: 0, width: 250, height: 21))
label.center = CGPoint(x: w+5, y: h)
label.textAlignment = .center
label.text = "Please confirm that you are connected to the web"
label.font = label.font.withSize(20)
label.tintColor = UIColor.gray
let imageViewObject = UIImageView(frame:CGRect(x: w-25, y: h-85, width: 60, height: 60))
imageViewObject.image = UIImage(named:"ic_settings_input_antenna_48pt")?.withRenderingMode(.alwaysTemplate)
imageViewObject.tintColor = UIColor.gray
self.view.addSubview(imageViewObject)
self.view.addSubview(label)
}
You can check for the network notifications from reachability. Then post some sorts of notification so that we would know the network status
import UIKit
import ReachabilitySwift
class ViewController: UIViewController {
let reachability = Reachability()
override func viewDidLoad() {
super.viewDidLoad()
checkNetworkStatus()
}
fileprivate func checkNetworkStatus() {
if let reachability = reachability {
reachability.whenReachable = { [unowned self] reachable in
self.postNetworkStatusChangedNotification(to: true)
}
reachability.whenUnreachable = { [unowned self] unreachable in
self.postNetworkStatusChangedNotification(to: false)
}
//start the notifier
do {
try reachability.startNotifier()
}
catch {
print("Unable to start the network notifier")
}
}
}
func postNetworkStatusChangedNotification(to acquired:Bool) {
//we will post notification
if acquired {
print("\n=== Internet Acess Acquired ===\n")
}
else {
print("\n=== Lost Internet Access ===\n")
}
}
}
When this is started it will notify whenever network status has been changed.

Swift: starting and stopping the animation of an activity indicator inside a custom class

I want to put an activity indicator inside a custom class so I can start/stop it from any view controller.
The below code works when starting the activity indicator but not stopping, how can I do this?
static func activityIndicatorFunction(view: UIView, targetVC: UIViewController, animate: Bool) {
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
if animate == false {
activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
} else {
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 = targetVC.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
//UIApplication.shared.beginIgnoringInteractionEvents()
}
}
An example of starting the activity indicator, the animate parameter would be false if I wanted to stop it.
Utils.activityIndicatorFunction(view: view, targetVC: self, animate: true)
This is a perfect candidate for protocol extensions. I recently did this myself.
First create the protocol in a file, say ActivityIndicatorPresenter.swift
/// Used for ViewControllers that need to present an activity indicator when loading data.
public protocol ActivityIndicatorPresenter {
/// The activity indicator
var activityIndicator: UIActivityIndicatorView { get }
/// Show the activity indicator in the view
func showActivityIndicator()
/// Hide the activity indicator in the view
func hideActivityIndicator()
}
Create a protocol extension (in the same file...or a different one)
public extension ActivityIndicatorPresenter where Self: UIViewController {
func showActivityIndicator() {
DispatchQueue.main.async {
self.activityIndicator.activityIndicatorViewStyle = .whiteLarge
self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 80, height: 80) //or whatever size you would like
self.activityIndicator.center = CGPoint(x: self.view.bounds.size.width / 2, y: self.view.bounds.height / 2)
self.view.addSubview(self.activityIndicator)
self.activityIndicator.startAnimating()
}
}
func hideActivityIndicator() {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.activityIndicator.removeFromSuperview()
}
}
}
Any view controller can then conform to the protocol
class MyViewController: UIViewController, ActivityIndicatorPresenter {
/// Make sure to add the activity indicator
var activityIndicator = UIActivityIndicatorView()
//Suppose you want to load some data from the network in this view controller
override func viewDidLoad() {
super.viewDidLoad()
showActivityIndicator() //Wow you can use this here!!!
getSomeData { data in
//do stuff with data
self.hideActivityIndicator()
}
}
What I suggest is to implement them as two separated methods, also to add them into extension of UIViewController, as follows:
UIViewController extension:
extension UIViewController {
func showActivityIndicator() {
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 = view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = .WhiteLarge
activityIndicator.startAnimating()
//UIApplication.shared.beginIgnoringInteractionEvents()
activityIndicator.tag = 100 // 100 for example
// before adding it, you need to check if it is already has been added:
for subview in view.subviews {
if subview.tag == 100 {
print("already added")
return
}
}
view.addSubview(activityIndicator)
}
func hideActivityIndicator() {
let activityIndicator = view.viewWithTag(100) as? UIActivityIndicatorView
activityIndicator?.stopAnimating()
// I think you forgot to remove it?
activityIndicator?.removeFromSuperview()
//UIApplication.shared.endIgnoringInteractionEvents()
}
}
I assume that you want to always show/hide the activityIndicator it to ViewController.view, if it is not, you might need let it an activityIndicator of UIView instead of UIViewController.
Usage:
For example, consider that you have two IBActions, the first shows the activity indicator and the other one hides it, they should be like:
#IBAction func show(sender: AnyObject) {
showActivityIndicator()
}
#IBAction func hide(sender: AnyObject) {
hideActivityIndicator()
}
I found it best just to set a 'tag' to the activity indicator and then reference to that when stopping the animation, using one function to hide and one to show as Ahmad suggested. Ahmad F's answer and Guillermo's answer seem good as well.
Show/hide functions inside my Utils.swift file:
static func showActivityIndicator(view: UIView, targetVC: UIViewController) {
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
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 = targetVC.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
activityIndicator.tag = 1
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
}
static func hideActivityIndicator(view: UIView) {
let activityIndicator = view.viewWithTag(1) as? UIActivityIndicatorView
activityIndicator?.stopAnimating()
activityIndicator?.removeFromSuperview()
UIApplication.shared.endIgnoringInteractionEvents()
}
Calling show function:
Utils.showActivityIndicator(view: view, targetVC: self)
Calling hide function:
Utils.hideActivityIndicator(view: view)

Starting an action when View Controller Loads

How do I start an action when the view controller loads on screen?
I've managed to do the function I want with an #IBAction but I don't want a button press for the action to happen, I want it to start the action when the page loads
any thoughts?
class ViewController: UIViewController {
var progress: KDCircularProgress!
#IBOutlet weak var Label1: UILabel!
var LabelText = String()
var scorestart = 1.0
var anglepercent = 3.6
override func viewDidLoad() {
super.viewDidLoad()
Label1.text = LabelText
view.backgroundColor = UIColor(white: 0.22, alpha: 1)
progress = KDCircularProgress(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
progress.startAngle = -90
progress.progressThickness = 0.2
progress.trackThickness = 0.3
progress.clockwise = true
progress.gradientRotateSpeed = 10
progress.roundedCorners = false
progress.glowMode = .Forward
progress.glowAmount = 0.9
progress.setColors(UIColor.yellowColor())
progress.center = CGPoint(x: view.center.x, y: view.center.y + 25)
view.addSubview(progress)
}
#IBAction func Animate(sender: AnyObject) {
progress.angle = Double(scorestart * anglepercent)
progress.animateFromAngle(0, toAngle: 270, duration: 2) {
completed in
if completed {
print("animation stopped, completed")
} else {
print("animation stopped, was interrupted")
}
Use :-
Basic idea here is that whenever your view will load corresponding class will look up to viewWillAppear(animated: Bool) function and if it's present in the code it will execute all the code in it.The moment that particular view is about to appear on your UI, your code block in viewWillAppear(animated: Bool) will get called.
class ViewController: UIViewController {
var progress: KDCircularProgress!
#IBOutlet weak var Label1: UILabel!
var LabelText = String()
var scorestart = 1.0
var anglepercent = 3.6
override func viewDidLoad() {
super.viewDidLoad()
Label1.text = LabelText
view.backgroundColor = UIColor(white: 0.22, alpha: 1)
}
override func viewWillAppear(animated :Bool) {
super.viewWillAppear(animated)
progressActn()
//Setting up your progress layer
animateActn()
//Animating that progress layer
}
#IBAction func Animate(sender: AnyObject) {
animateActn()
}
func animateActn(){
progress.angle = Double(scorestart * anglepercent)
progress.animateFromAngle(0, toAngle: 270, duration: 2) {
completed in
if completed {
print("animation stopped, completed")
} else {
print("animation stopped, was interrupted")
}
}
}
func progressActn(){
progress = KDCircularProgress(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
progress.startAngle = -90
progress.progressThickness = 0.2
progress.trackThickness = 0.3
progress.clockwise = true
progress.gradientRotateSpeed = 10
progress.roundedCorners = false
progress.glowMode = .Forward
progress.glowAmount = 0.9
progress.setColors(UIColor.yellowColor())
progress.center = CGPoint(x: view.center.x, y: view.center.y + 25)
view.addSubview(progress)
}
}

How to show activity indicator while tableView loads?

When I switch between my tabs it loads some seconds and I want to know that my data is loading. For that I decided to add an activity indicator.
I wrote a little function:
func showActivityIndicator() {
dispatch_async(dispatch_get_main_queue()) {
self.spinner = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
self.spinner.frame = CGRect(x: 0.0, y: 0.0, width: 80.0, height: 80.0)
self.spinner.center = CGPoint(x:self.loadingView.bounds.size.width / 2, y:self.loadingView.bounds.size.height / 2)
self.loadingView.addSubview(self.spinner)
self.view.addSubview(self.loadingView)
self.spinner.startAnimating()
}
}
that will show my indicator. And try to use it when I tapped from my infoController to button:
#IBAction func goToMainFromInfo(sender: AnyObject) {
self.showActivityIndicator()
self.performSegueWithIdentifier("fromMainToInfoWActivity", sender: nil)
self.hideActivityIndicator()
}
}
I show it before segue perform and hide after. It doesn't help me. When I did try to use sync:
#IBAction func goToMainFromInfo(sender: AnyObject) {
dispatch_async(dispatch_get_main_queue()) {
self.showActivityIndicator()
self.performSegueWithIdentifier("fromMainToInfoWActivity", sender: nil)
self.hideActivityIndicator()
}
}
But it doesn't help too. When I press to tab it opacity becomes 0.5 and I wait while it loading. But I do not see my activity indicator.
What is the problem?
Just try this:
var indicator = UIActivityIndicatorView()
func activityIndicator() {
indicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 40, 40))
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
indicator.center = self.view.center
self.view.addSubview(indicator)
}
And where you want to start animating
indicator.startAnimating()
indicator.backgroundColor = UIColor.whiteColor()
For stop:
indicator.stopAnimating()
indicator.hidesWhenStopped = true
Note: Avoid the calling of start and stop at the same time. Just give some conditions.
SWIFT : 4.2
Just try this:
var indicator = UIActivityIndicatorView()
func activityIndicator() {
indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
indicator.style = UIActivityIndicatorView.Style.gray
indicator.center = self.view.center
self.view.addSubview(indicator)
}
And where you want to start animating
activityIndicator()
indicator.startAnimating()
indicator.backgroundColor = .white
For stop:
indicator.stopAnimating()
indicator.hidesWhenStopped = true
Swift 3.0
// UIView Extension
fileprivate var ActivityIndicatorViewAssociativeKey = "ActivityIndicatorViewAssociativeKey"
public extension UIView {
var activityIndicatorView: UIActivityIndicatorView {
get {
if let activityIndicatorView = getAssociatedObject(&ActivityIndicatorViewAssociativeKey) as? UIActivityIndicatorView {
return activityIndicatorView
} else {
let activityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
activityIndicatorView.activityIndicatorViewStyle = .gray
activityIndicatorView.color = .gray
activityIndicatorView.center = center
activityIndicatorView.hidesWhenStopped = true
addSubview(activityIndicatorView)
setAssociatedObject(activityIndicatorView, associativeKey: &ActivityIndicatorViewAssociativeKey, policy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return activityIndicatorView
}
}
set {
addSubview(newValue)
setAssociatedObject(newValue, associativeKey:&ActivityIndicatorViewAssociativeKey, policy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
// NSObject Extension
public extension NSObject {
func setAssociatedObject(_ value: AnyObject?, associativeKey: UnsafeRawPointer, policy: objc_AssociationPolicy) {
if let valueAsAnyObject = value {
objc_setAssociatedObject(self, associativeKey, valueAsAnyObject, policy)
}
}
func getAssociatedObject(_ associativeKey: UnsafeRawPointer) -> Any? {
guard let valueAsType = objc_getAssociatedObject(self, associativeKey) else {
return nil
}
return valueAsType
}
}
start animation
tableView.activityIndicatorView.startAnimating()
stop animation
tableView.activityIndicatorView.stopAnimating()
You can find more code in Magic
Swift 2+
class ViewController: UITableViewController {
weak var activityIndicatorView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
tableView.backgroundView = activityIndicatorView
self.activityIndicatorView = activityIndicatorView
activityIndicatorView.startAnimating()
}
...
}
I use two extension methods to add an UIActivityIndicatorView as the backgroundView of the tableview.
extension UITableView {
func showActivityIndicator() {
DispatchQueue.main.async {
let activityView = UIActivityIndicatorView(style: .medium)
self.backgroundView = activityView
activityView.startAnimating()
}
}
func hideActivityIndicator() {
DispatchQueue.main.async {
self.backgroundView = nil
}
}
}
You can show/hide it like this.
tableView.showActivityIndicator()
tableView.hideActivityIndicator()
This code can help you :)
let indicator:UIActivityIndicatorView = UIActivityIndicatorView (activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
indicator.color = UIColor .magentaColor()
indicator.frame = CGRectMake(0.0, 0.0, 10.0, 10.0)
indicator.center = self.view.center
self.view.addSubview(indicator)
indicator.bringSubviewToFront(self.view)
indicator.startAnimating()
SWIFT
Place this below your class:
let indicator:UIActivityIndicatorView = UIActivityIndicatorView (activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
Place this in your loadView():
indicator.color = UIColor .magentaColor()
indicator.frame = CGRectMake(0.0, 0.0, 10.0, 10.0)
indicator.center = self.view.center
self.view.addSubview(indicator)
indicator.bringSubviewToFront(self.view)
indicator.startAnimating()
In my case, I am requesting json objects through a func request, so I placed this at the end of that function to remove the activity indicator once the data loads:
self.indicator.stopAnimating()
self.indicator.hidesWhenStopped = true
Another approach, In my code I added an extension for UITableView (Swift 2.3) :
extension UITableView {
func activityIndicator(center: CGPoint = CGPointZero, loadingInProgress: Bool) {
let tag = 12093
if loadingInProgress {
var indicator = UIActivityIndicatorView()
indicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 40, 40))
indicator.tag = tag
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
indicator.color = //Your color here
indicator.center = center
indicator.startAnimating()
indicator.hidesWhenStopped = true
self.superview?.addSubview(indicator)
}else {
if let indicator = self.superview?.viewWithTag(tag) as? UIActivityIndicatorView { {
indicator.stopAnimating()
indicator.removeFromSuperview()
}
}
}
}
Note : My tableview is embedded in a UIView (superview)
Update Swift 4.2:
1.call the activityIndicator function on viewDidLoad
eg:
var indicator = UIActivityIndicatorView()
override func viewDidLoad() {
//Initializing the Activity Indicator
activityIndicator()
//Starting the Activity Indicator
indicator.startAnimating()
//Call Your WebService or API
callAPI()
}
Here is the Code For Adding ActivityIndicator as Subview
func activityIndicator() {
indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
indicator.style = UIActivityIndicatorView.Style.whiteLarge
indicator.color = .red
indicator.center = self.view.center
self.view.addSubview(indicator)
}
2. Do UI related Operations or API Call and stop activity indicator
func callAPI() {
YourModelClass.fetchResult(someParams,
completionHandler: { (response) in
//Do necessary UIUpdates
//And stop Activity Indicator
self.indicator.stopAnimating()
})
}
func setupSpinner(){
spinner = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height:40))
spinner.color = UIColor(Colors.Accent)
self.spinner.center = CGPoint(x:UIScreen.main.bounds.size.width / 2, y:UIScreen.main.bounds.size.height / 2)
self.view.addSubview(spinner)
spinner.hidesWhenStopped = true
}
Using "lazy var". It's better than function
fileprivate lazy var activityIndicatorView: UIActivityIndicatorView = {
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicatorView.hidesWhenStopped = true
// Set Center
var center = self.view.center
if let navigationBarFrame = self.navigationController?.navigationBar.frame {
center.y -= (navigationBarFrame.origin.y + navigationBarFrame.size.height)
}
activityIndicatorView.center = center
self.view.addSubview(activityIndicatorView)
return activityIndicatorView
}()
Just start the spinner anywhere
like this
func requestData() {
// Request something
activityIndicatorView.startAnimating()
}
#brocolli's answer for swift 4.0. You have to use objc_ before getting or setting associated objects. According to the documentation, The APIs of getting and setting the associated object in Swift are:
func objc_getAssociatedObject(object: AnyObject!,
key: UnsafePointer<Void>
) -> AnyObject!
func objc_setAssociatedObject(object: AnyObject!,
key: UnsafePointer<Void>,
value: AnyObject!,
policy: objc_AssociationPolicy)
Implementation:
import UIKit
fileprivate var ActivityIndicatorViewAssociativeKey = "ActivityIndicatorViewAssociativeKey"
extension UIView {
var activityIndicatorView: UIActivityIndicatorView {
get {
if let activityIndicatorView = objc_getAssociatedObject(self, &ActivityIndicatorViewAssociativeKey) as? UIActivityIndicatorView {
return activityIndicatorView
} else {
let activityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
activityIndicatorView.activityIndicatorViewStyle = .gray
activityIndicatorView.color = .gray
activityIndicatorView.center = center
activityIndicatorView.hidesWhenStopped = true
addSubview(activityIndicatorView)
objc_setAssociatedObject(self, &ActivityIndicatorViewAssociativeKey, activityIndicatorView, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return activityIndicatorView
}
}
set {
addSubview(newValue)
objc_setAssociatedObject(self, &ActivityIndicatorViewAssociativeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
In order to place the UIActivityIndicator in foreground, even over the eventual UITableViewController separators, I have adopted this solution.
I have add the UIActivityIndicator programmatically, and add it as a subview of my UINavigationController
var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
// Code
// .... omissis
// Set activity indicator
activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
activityIndicator.color = UIColor.darkGray
activityIndicator.center = tableView.center
activityIndicator.hidesWhenStopped = true
activityIndicator.stopAnimating()
self.navigationController?.view.addSubview(activityIndicator)
}
I have just start & stop it when needed (in my case):
func animateActivityIndicator(_ sender: Any ) {
guard let vc = sender as? UIViewController else { return }
if let v = vc as? MyTableViewController {
if v.activityIndicator.isAnimating {
v.activityIndicator.stopAnimating()
} else {
v.activityIndicator.startAnimating()
}
}
// Others UIViewController or UITableViewController follows...
// all of them exhibits an activityIndicator variable
// implemented programmatically or with the storyboard
}
PS. My environment is Xcode 10.0, iOS 12.0

Resources