views:

40

answers:

1

I'm trying to show a UIProgressView on top of a table view after certain user interactions. In my table view, if the user taps one particular cell, I slide up a UIView that contains a toolbar with bar items and a picker view (it very much behaves like an action sheet). When I do this, I'm adding the view offscreen to the current view, and then animating the slide-in. When the user makes a selection and taps "done", the view slides back out. At that point, the progress view is supposed to appear and update as some things are happening in the background.

The problem I'm having is that after the "alert sheet" UIView is slided out of the current view, nothing happens in the UI for a good while. When I show the sliding view, I do this:

[[[UIApplication sharedApploication] keyWindow] addSubview:slidingView];
CGRect newFrame = CGRectMake(...);
[UIView animateWithDuration:0.3
                 animations:^{
                     slidingView.frame = newFrame;
                 }];

when the user taps the 'done' button in the sliding view, this action method is invoked:

- (void) done {
    NSNumber *row = GetSelectedRowSomehow();

    [self dismiss:@selector(doneCallback:) withObject:row];
}

- (void) dismiss:(SEL)cb withObject:(id)obj {
    [UIView animateWithDuration:0.3
                     animations:^{
                         slidingView.frame = CGRectMake(...);
                     }
                     completion:^(BOOL finished) {
                         [self.delegate performSelectorOnMainThread:cb
                                                         withObject:obj
                                                      waitUntilDone:NO];
                         [slidingView performSelectorOnMainThread:@selector(removeFromSuperview:)
                                                       withObject:nil
                                                    waitUntilDone:NO];
                     }];
}

The callback that gets called here:

- (void) doneCallback {
    self.dialogView.hidden = NO;
    self.progressView.progress = 0;

    for (float i = 0; i < 1.0; i += 0.1) {
        self.progressView.progress += i;
        sleep(0.5);
    }
}

In my case, dialogView doesn't appear until after callback has completed. Why wouldn't it just update the display immediately after its hidden property was set to NO?

+2  A: 

The display is updated in the main thread once your code finishes executing, so by blocking the main thread you're preventing that happening.

One reason for that is that the updates are batched together so they can be redrawn efficiently.

One solution/workaround would be to use performSelector:afterDelay:0.01 to in doneCallback after the hidden=NO, moving the following code into a new selector that runs after the delay.

Alternatively you could run the code within "doneCallback" as a background operation instead (though you cannot directly update UIKit from the background thread, so you'd have to send messages to the main thread for any display updates).

JosephH
Thanks. I figured it might be something like that, but I tried a couple of things to try and nudge the UI into updating itself that didn't work, so I was at a bit of a loss because I was just expecting it to work. It's helpful to know that the UI batches updates (I should have known). I ended up pushing most of this to a dispatch queue and now the UI is updating like it should.
Ben Collins