0

I have a login button in my app. While logging in there is also some data retrieved from the server in a background thread so it can take a few seconds. To let the user know he/she has to wait I display a simple activity indicator (the round loading thingy apply provides). The only problem I have now is that the user can spam the button.
I tried putting [loginButton setUserInteractionEnabled:false]; in the onclick handler and [loginButton setUserInteractionEnabled:true]; in my oncomplete handler for the data fetching. But as soon as user interaction is set to true again, all button press events that would otherwise have fired in the meantime still fire (all at once).
Since that didn't work I created a boolean loginButtonActive and set that to false when the user presses the button. I put the rest of the button press action inside an if (loginButtonActive){} and in the oncomplete I set the boolean to true again. This leaves me with the same problem, the messages from the button presses are sometimes delayed and will still 'press' the button again once the boolean is true again.

Here is my code so far:

- (IBAction)onLogin:(id)sender {
    NSLog(@"button press");
    if (loginButtonActive) {
        NSLog(@"button was active");
        loginButtonActive = false;
        if ([usernameField.text isEqualToString:@""]) {
            [GeneralFunctions showError:@"Voer uw gebruikersnaam in" withTitle:@"Fout bij inloggen"];
        }
        else if ([passwordField.text isEqualToString:@""]) {
            [GeneralFunctions showError:@"Voer uw wachtwoord in" withTitle:@"Fout bij inloggen"];
        } else {
            [UIView beginAnimations:nil context:nil];
            [UIView setAnimationDuration:0.5f];

            [loginActivity setAlpha:1.0f];

            loginButtonWidth = loginButton.frame.size.width;
            loginButtonX = loginButton.frame.origin.x;

            CGRect frame = loginButton.frame;
            frame.origin.x += frame.size.width;
            frame.size.width = frame.size.height;
            frame.origin.x -= frame.size.width;
            loginButton.frame = frame;

            loginButton.text = @"";

            [UIView commitAnimations];

            dispatch_async(dispatch_get_main_queue(), ^{
                //Login and fetch some data from the web service
                me = [[User alloc] initWithLogin:[usernameField text] password:[GeneralFunctions md5:backing] version:@"1.0.0.1"];
                [self restoreLoginButton];
                if (me != nil) {
                    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
                    [prefs removeObjectForKey:@"username"];
                    [prefs removeObjectForKey:@"password"];
                    if (rememberSwitch.on) {
                        [prefs setObject:usernameField.text forKey:@"username"];
                        [prefs setObject:backing forKey:@"password"];
                    }
                    [prefs synchronize];
                    [self loginSuccess];

                    loginButton.text = @"Ingelogd";
                } else {
                    loginButton.text = @"Inloggen";
                }
            });
        }
    } else {
        NSLog(@"button was not active");
    }
}

-(void)restoreLoginButton {
    NSLog(@"restoring button");
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.5f];

        [loginActivity setAlpha:0.0f];

        CGRect frame = loginButton.frame;
        frame.size.width = loginButtonWidth;
        frame.origin.x = loginButtonX;
        loginButton.frame = frame;

        [UIView commitAnimations];
        loginButtonActive = true;
    });
}

Here is my log from spamming the button. I stopped spamming before it was done with fetching the data. I let it run untill it stopped logging. Notice how the button press logs directly follow the restoring button logs.

2014-04-22 11:45:32.645 Wondzorg[3097:60b] button press
2014-04-22 11:45:32.648 Wondzorg[3097:60b] button was active
2014-04-22 11:45:36.520 Wondzorg[3097:60b] restoring button
2014-04-22 11:45:36.534 Wondzorg[3097:60b] button press
2014-04-22 11:45:36.534 Wondzorg[3097:60b] button was active
2014-04-22 11:45:43.283 Wondzorg[3097:60b] restoring button
2014-04-22 11:45:43.290 Wondzorg[3097:60b] button press
2014-04-22 11:45:43.290 Wondzorg[3097:60b] button was active
2014-04-22 11:45:45.680 Wondzorg[3097:60b] restoring button
2014-04-22 11:45:45.687 Wondzorg[3097:60b] button press
2014-04-22 11:45:45.688 Wondzorg[3097:60b] button was active
2014-04-22 11:45:48.035 Wondzorg[3097:60b] restoring button
2014-04-22 11:45:48.041 Wondzorg[3097:60b] button press
2014-04-22 11:45:48.042 Wondzorg[3097:60b] button was active
2014-04-22 11:45:50.308 Wondzorg[3097:60b] restoring button
2014-04-22 11:45:50.315 Wondzorg[3097:60b] button press
2014-04-22 11:45:50.316 Wondzorg[3097:60b] button was active

How can I make sure the button does not respond to anything while I'm still fetching data?

Kevin
  • 2,739
  • 33
  • 57

4 Answers4

1

Try this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    me = [[User alloc] initWithLogin:[usernameField text] password:[GeneralFunctions md5:backing] version:@"1.0.0.1"];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self restoreLoginButton];
        if (me != nil) {
            NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
            [prefs removeObjectForKey:@"username"];
            [prefs removeObjectForKey:@"password"];
            if (rememberSwitch.on) {
                [prefs setObject:usernameField.text forKey:@"username"];
                [prefs setObject:backing forKey:@"password"];
            }
            [prefs synchronize];
            [self loginSuccess];
            loginButton.text = @"Ingelogd";
        } else {
            loginButton.text = @"Inloggen";
        }
    });
});

Hope this helps... :)

Rashad
  • 11,057
  • 4
  • 45
  • 73
0

There is setUserInteractionEnabled as method. But I think thats just because its extending from UIView. But this is not the way to en / disable an UIButton from being clicked. For a more precise understanding of the class hierarchy of UIButton read this SO question. You should try this

[myButton setEnabled:NO];

and to make it clickable again

[myButton setEnabled:YES];
Community
  • 1
  • 1
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
0

Have you tried to put the loginButtonActive = false; or the UserInteractionEnabled = NO in the main thread also? Maybe this solves the delay.

Rashad
  • 11,057
  • 4
  • 45
  • 73
Philipp Otto
  • 4,061
  • 2
  • 32
  • 49
0

What does this code me = [[User alloc] initWithLogin:[usernameField text] password:[GeneralFunctions md5:backing] version:@"1.0.0.1"]; actually do? Is it calling an asynchronous function? If so its going to return straightaway and you are not really then disabling the button

You either ned to crate a protocol from the login method that you can use to tell the UI that the login has finished and then enable your button or use Notification Center

Flexicoder
  • 8,251
  • 4
  • 42
  • 56