0

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()
}
Robert
  • 1,279
  • 1
  • 13
  • 22
  • @AnthonyDito UIActivityIndicatorView has got no method startActivityIndicator() – oyvindhauge Oct 08 '15 at 22:35
  • @AnthonyDito I figured that's what you meant. He is already calling that method though. – oyvindhauge Oct 08 '15 at 22:37
  • And it's weird...calling the method exactly like i'm doing here from `viewDidLoad()` works without issue. – Robert Oct 08 '15 at 22:39
  • Are you sure you're not hitting one of the two conditionals before the call to Parse? Did you test with breakpoints? – oyvindhauge Oct 08 '15 at 22:44
  • Yeah, I set breakpoints to make sure it doesn't him them, as well as a breakpoint to make sure the activity indicator code is executing. – Robert Oct 08 '15 at 22:46
  • It's almost like the view isn't "refreshed" until the function ends. – Robert Oct 08 '15 at 22:50
  • Where do you actually create loadingView and activityIndicator? – rmaddy Oct 08 '15 at 23:03
  • Sure that your validateEmail and validatePassword checks are not stopping it right after being started? If so it would not show. As an aside, you do not remove the `activityIndicator` from its superview in `stopActivityIndicator`, so the next start will add it again. – Rory McKinnel Oct 08 '15 at 23:11
  • Yeah, unfortunately, i'm sure they don't stop it. Thanks for the heads up on the other issue. – Robert Oct 08 '15 at 23:18
  • You could try calling `view.layoutIfNeeded()` before you call `startAnimating()` – Rory McKinnel Oct 08 '15 at 23:41
  • Thanks Rory, but that didn't work. @matt had the best solution below. – Robert Oct 09 '15 at 00:02

1 Answers1

1

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.

Community
  • 1
  • 1
matt
  • 447,615
  • 74
  • 748
  • 977
  • This is the way to do it. –  Oct 08 '15 at 23:20
  • You're just showing off now @matt :) Worked perfect. Do you know why I need the delay? I'm trying to learn in addition to solving my issue. – Robert Oct 09 '15 at 00:01
  • Of course I know why. And so do you. You gave the reason yourself, in a comment. – matt Oct 09 '15 at 00:03
  • I'm assuming you mean the "refreshed" comment, but I validated that startActivityIndicator finished before the first validation check started. I realize you've got better things to do, and more people to save :) If you get time to explain a little, great. If not, no worries, and thanks again! – Robert Oct 09 '15 at 00:52
  • I've written an entire book that explains this to you. :) http://www.apeth.com/iOSBook/ch17.html#_drawing_animation_and_threading The delay gives the redraw moment (the end of the current transaction) a chance to show the activity indicator and start it spinning on the animation server thread. – matt Oct 09 '15 at 00:57
  • The first paragraph of that explains it perfectly. Already been looking at your new book (avail 10-12) on Amazon. Very good sir. Looks like I've got some new reading to do :) – Robert Oct 09 '15 at 01:04