Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spin activity indicator after delay during lengthy operations

I am developing an iOS app with a UITableView which at some stage adds or removes a bunch of its rows. Since there are a large number of rows, this operation can take long. However, I cannot easily determine if it will take a long time or not.

I would like to display a UIActivityIndicator (spinner) only if this operation is taking a long time. The way that I've always done this, is to start the lengthy operation, and after some delay (say 0.5 seconds) we test if the operation is still running, and if it is, we start displaying the UIActivityIndicator.

If you can run the lengthy operation in a background thread, this is no problem. However, this particular case is tricky because the lengthy operation (deleteRowsAtIndexPaths:withRowAnimation:) must run in the main thread (if I run this method in a background thread, the app crashes occasionally when the background thread tries to update the UI).

The latest thing that I've tried was in the lines of this:

- (void) manipulateTableView
{
    stillBusy = YES;
    [self performSelectorInBackground:@selector(waitBeforeShowingBusyIndicatorForView:) withObject:view];
    .
    .
    .
    [self performSelectorOnMainThread:@selector(deleteRowsAtIndexPathsWithRowAnimation:) withObject:args waitUntilDone:YES];
    stillBusy = NO;
}

- (void) waitBeforeShowingBusyIndicatorForView:(UIView*) view
{
    usleep((int) (BUSY_INDICATOR_DELAY * 1000000));
    if (stillBusy)
        [self performSelectorOnMainThread:@selector(showBusyIndicatorForView:) withObject:view waitUntilDone:NO];
}

Because showBusyIndicatorForView: manipulates the UI, it must be called on the main thread, otherwise the app might crash.

When deleteRowsAtIndexPaths:withRowAnimation: takes very long, the delay in waitBeforeShowingBusyIndicatorForView: expires and the performSelectorOnMainThread:... method is called and returns immediately. But then the showBusyIndicatorForView: method is called only after the call to deleteRowsAtIndexPaths:withRowAnimation: completes, which defeats the purpose.

I think I understand why this happens. The deleteRowsAtIndexPaths:withRowAnimation: method runs in an iteration of the main loop, and while it's running, the call to showBusyIndicatorForView: is queued as a message of the main loop. Only after main loop completes executing deleteRowsAtIndexPaths:withRowsAnimation: it polls the queue for the next message and then starts executing showBusyIndicatorForView:.

Is there any way of getting this thing to work properly. Is it perhaps possible to interrupt the main loop and make it execute showBusyIndicatorForView: immediately?

like image 746
bgh Avatar asked Dec 05 '25 06:12

bgh


1 Answers

Two options leap out at me:

  1. Modify your long running foreground routine so that it is broken into multiple blocks that you repeatedly dispatch each step of the process separately to the main queue. And do it in such a way that you design the process so it dispatches the each portion of the slow foreground task and only dispatches the next step to the main queue when the previous portion completes. (Don't just queue up a ton of processes for the main queue.) That way, you give the spinner a chance dovetail into the other jobs as they're queued up. That might not be possible if you're animating your deletions, but then again, I can't imagine it would take that long to do a single deleteRowsAtIndexPaths.

  2. Probably better, if the update was taking that long, I'd ask why that is. The deleteRowsAtIndexPaths shouldn't account for that. Specifically, don't issue multiple deleteRowsAtIndexPaths, but rather build a NSMutableArray of the rows to delete, and then delete them in a single UI call. I just deleted 998 rows (including their underlying model objects) and it was nearly instantaneous.

I'd lean towards option 2 if possible.

like image 57
Rob Avatar answered Dec 07 '25 21:12

Rob



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!