1

I have a UIButton setup which I wanted to use as a way to tell the user something was happening instead of using a loader just for this one instance.

So for example the UIControlStateNormal has:
Background Green
Title text white
Title - Click to Send

Then UIControlStateDisabled has:
Background White
Title text green
Title - Sending

Then when the button is clicked we update the button to enabled = NO;.

Issue
The issue I notice is that when the button is changed to disabled (enabled NO) the changes to the title text, color, background are not completed until all the method calls etc are complete, so too late.

I read in some other threads to use btn layoutIfNeeded and/or btn setNeedsLayout. These are what actually force the button to be updated, but do not seem to happen until after all the other calls/methods are complete. Therefore its too late to update the button text etc.

Any ideas on how to make the button update its state and attributes straight away then complete the other tasks? I have checked to make sure its on the main thread and all the updates/calls are so that is not the issue.

EDIT
Basically when the button is tapped we update the state:

- (IBAction)sendFeedbackButtonPressed:(id)sender {
    DebugLog(@"selected: %@",self.sendFeedbackButton.selected ? @"Yes" : @"No");
    DebugLog(@"highlighted: %@",self.sendFeedbackButton.highlighted ? @"Yes" : @"No");

    dispatch_async(dispatch_get_main_queue(), ^{
        [self shouldFeedbackButtonBeEnabled:NO];
        DebugLog(@"enabled: %@",self.sendFeedbackButton.enabled ? @"Yes" : @"No");
    });

    dispatch_async(dispatch_get_main_queue(), ^{
        NSString *errorMessage = [self validateForm];

        if (errorMessage) {
            [[[UIAlertView alloc] initWithTitle:nil message:errorMessage delegate:nil cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(@"Feedback Form Validation Error Ok Button", nil), nil] show];
            [self shouldFeedbackButtonBeEnabled:YES];
            return;
        }

        //TODO: send it
        // Send the form values to the server here.
        [self sendFeedbackToParse];
    });
}

-(void)setupSendFeedbackButton {
    UIColor *mainColor = [UIColor colorWithRed:41/255.0f green:128/255.0f blue:185/255.0f alpha:1];

    self.sendFeedbackButton.clipsToBounds = YES;
    self.sendFeedbackButton.layer.cornerRadius = 5.0f;
    self.sendFeedbackButton.layer.borderWidth = 2;
    self.sendFeedbackButton.layer.borderColor = mainColor.CGColor;

    // Disabled State
    [self.sendFeedbackButton setBackgroundImage:[WTNUtility imageFromColor:[UIColor whiteColor]] forState:UIControlStateDisabled | UIControlStateHighlighted];
    [self.sendFeedbackButton setTitle:NSLocalizedString(@"Feedback Form Send Feedback Button - Sending feedback disabled state", nil) forState:UIControlStateDisabled | UIControlStateHighlighted];
    [self.sendFeedbackButton setTitleColor:mainColor forState:UIControlStateDisabled | UIControlStateHighlighted];

    // Normal State
    [self.sendFeedbackButton setBackgroundImage:[WTNUtility imageFromColor:mainColor] forState:UIControlStateNormal];
    [self.sendFeedbackButton setTitle:NSLocalizedString(@"Feedback Form Send Feedback Button Title", nil) forState:UIControlStateNormal];
    [self.sendFeedbackButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}

-(void)shouldFeedbackButtonBeEnabled:(BOOL)decision {
    BOOL currentState = self.sendFeedbackButton.enabled;

    if (currentState == decision) {
        return;
    } else {
        self.sendFeedbackButton.enabled = decision;
        [self.sendFeedbackButton layoutIfNeeded];
    }
}

-(void)sendFeedbackToParse {
    DebugLog(@"%s",__PRETTY_FUNCTION__);
    DebugLog(@"button is enabled: %@",self.sendFeedbackButton.enabled ? @"Yes" : @"No");
    DebugLog(@"highlighted: %@",self.sendFeedbackButton.highlighted ? @"Yes" : @"No");
//    sleep(10);

//    self.sendFeedbackButton.enabled = YES;
}
StuartM
  • 6,743
  • 18
  • 84
  • 160

1 Answers1

3

You're on the right track about checking the main queue, but your additional processing has to finish before the UI gets a chance to update.

If your code is friendly to this approach, use two dispatch blocks back to the main queue. The first would be your UI-state settings, the second, your remaining processing. This lets the UI state actually update before your additional processing complete.

Pseudocode:

- (IBAction) handlePress:(id)sender {
  dispatch_async(dispatch_get_main_queue(), ^{
// Do your state update (button disabled, etc)
                        });

  dispatch_async(dispatch_get_main_queue(), ^{
// Do your further processing - AFTER the ui has been updated
                        });
}
Brad Brighton
  • 2,179
  • 1
  • 13
  • 15
  • Thanks, how do you mean? The process that we run is not too intensive and friendly enough. Basically we do some validation/error showing if it passes we send the data somewhere. How would you have two dispatch blocks on the main? I can update the question with some code for examples – StuartM Jan 18 '15 at 19:25
  • Looks like we were editing at the same time. Take a look at the modified answer; is this clearer or is more needed? – Brad Brighton Jan 18 '15 at 19:32
  • Thanks, do I need to do any callbacks to ensure the UI is updated, or just using the two blocks means one is completed on the main thread before the second task on the main thread would begin?> – StuartM Jan 18 '15 at 19:33
  • Unless you have something more complicated going on, you won't need to do anything additional. By using the two blocks, you're giving the state change a chance to actually do its thing to the UI before you wheedle away into the additional processing. – Brad Brighton Jan 18 '15 at 19:34
  • Unfortuantely it makes no difference. Even if I put a break at the start of the second main queue call, the update to the button state is still not completed. Even though it is just changing the enabled to NO and applying the relevant state controlled items like titleLabel, backgroundColor and so on... Its like they are queued and not completed until the very end. – StuartM Jan 18 '15 at 19:42
  • That's peculiar; maybe update your code sample with the change in case it's a code positioning thing? I don't have anything else to suggest with the available info. – Brad Brighton Jan 18 '15 at 19:46
  • I know strange right, Ive added an edit which includes the whole lot. Button setup to the relevant methods. We can take to chat if that's easier, I'm not sure... – StuartM Jan 18 '15 at 19:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69078/discussion-between-brad-brighton-and-stuartm). – Brad Brighton Jan 18 '15 at 19:55