0

I am using this piece of code to display an MBProgressHUD over the top of one of my views while I download data from a web-service, the only problem is that occasionally this code will cause the app to hang, doing nothing while the HUD displays "Downloading" and the screen is locked. Also, if I have something like a keyboard being displayed to the user when I press the refresh button (refresh button performs the download) then the application crashes on the line:

 [self.tableView reloadData];

My code:

//Checks for network connection then displays HUD while executing pullAndDisplayData method
- (IBAction) update {
    UIAlertView *errorView;

    if([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) {
        errorView = [[UIAlertView alloc] 
                     initWithTitle: @"Network Error" 
                     message: @"No Network connection availible!" 
                     delegate: self 
                     cancelButtonTitle: @"OK" otherButtonTitles: nil]; 
        [errorView show];
    }
    else
    {
        HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
        [self.navigationController.view addSubview:HUD];

        HUD.delegate = self;
        HUD.labelText = @"Downloading";
        HUD.minSize = CGSizeMake(135.f, 135.f);

        [HUD showWhileExecuting:@selector(pullAndDisplayData) onTarget:self withObject:nil animated:YES];
    }
}

//Downloads this users data from the web-service
- (void) pullAndDisplayData{
    // Indeterminate mode
    ExpensesDataDownloader *downloader = [[ExpensesDataDownloader alloc] init];
    [downloader pullAndDisplayData];

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if ([[defaults objectForKey:@"canExportCSVServer"] isEqualToString:@"1"])
    {

    }

    [self.tableView reloadData];

    // Switch to determinate mode
    HUD.mode = MBProgressHUDModeDeterminate;
    HUD.labelText = @"Updating";
    float progress = 0.0f;
    while (progress < 1.0f)
    {
        progress += 0.01f;
        HUD.progress = progress;
        usleep(15000);
    }
    // The sample image is based on the work by www.pixelpressicons.com, http://creativecommons.org/licenses/by/2.5/ca/
    // Make the customViews 37 by 37 pixels for best results (those are the bounds of the build-in progress indicators)
    HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
    HUD.mode = MBProgressHUDModeCustomView;
    HUD.labelText = @"Completed";
    sleep(2);
}

Any help would be much appreciated.

Jack

Janak Nirmal
  • 22,706
  • 18
  • 63
  • 99
Jack Nutkins
  • 1,555
  • 5
  • 36
  • 71

3 Answers3

2

pullAndDisplayData method is running on a separate thread. This is so that MBProgressHUD can use UI thread to show itself. You should always update your UI from main (UI) thread. Use performSelectorOnMainThread: method to call [self.tableView reloadData]; and other UI stuff. I am assuming that [downloader pullAndDisplayData]; is synchronous call.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
msk
  • 8,885
  • 6
  • 41
  • 72
1

From MBprogressHUD API

/** 
 * Shows the HUD while a background task is executing in a new thread, then hides the HUD.
 *
 * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
 * pool.
 *
 * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
 * @param target The object that the target method belongs to.
 * @param object An optional object to be passed to the method.
 * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
 * animations while (dis)appearing.
 */
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;

Since you are using this method, your pullAndDisplayData is executed in a new thread. This could cause the strange problem you have (I suppose). You are updating the UI elements from a background thread and this is not good. UI elements would be updated form the main thread. Use the background thread to downlaod data only.

Insetad of using it, try to use GCD (Grand Central Dispatch)

[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    // download operation here...
    dispatch_async(dispatch_get_main_queue(), ^{
        [MBProgressHUD hideHUDForView:self.view animated:YES];
        // reload data here...
    });
});

For further info see Usage section for the MBProgressHUD.

msk
  • 8,885
  • 6
  • 41
  • 72
Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
0

The problem might be occurring due to some memory issue, You try using SVProgressHUD for this, it is the extended version of MBProgressHUD:

You just need to do like this :

- (void) pullAndDisplayData{

    [SVProgressHUD showWithStatus:@"Downloading..."];

    // Indeterminate mode
    ExpensesDataDownloader *downloader = [[ExpensesDataDownloader alloc] init];
    [downloader pullAndDisplayData];

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if ([[defaults objectForKey:@"canExportCSVServer"] isEqualToString:@"1"])
    {

    }

    [self.tableView reloadData];

    // Switch to determinate mode
    HUD.mode = MBProgressHUDModeDeterminate;
    HUD.labelText = @"Updating";
    float progress = 0.0f;
    while (progress < 1.0f)
    {
        progress += 0.01f;
        HUD.progress = progress;
        usleep(15000);
    }
    // The sample image is based on the work by www.pixelpressicons.com, http://creativecommons.org/licenses/by/2.5/ca/
    // Make the customViews 37 by 37 pixels for best results (those are the bounds of the build-in progress indicators)
    HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
    HUD.mode = MBProgressHUDModeCustomView;
    HUD.labelText = @"Completed";
    sleep(2);

    [SVProgressHUD dismiss];
}

No need to allocate or release anything. It works just like that!!!

All the best!!!

Kanan Vora
  • 2,124
  • 1
  • 16
  • 26
  • Since I cannot recognize background thread/queue anywhere here in your example it looks like it would run within the main thread and block it, thus not showing the progress, since UI won't have a chance to run and update HUD. – Tomasz Stanczak Jul 10 '12 at 11:56
  • It was not my question, I am just commenting on your code, not using it. If you look at the question you'll see that it's intention was to run some code in the background showing HUD at the same time. – Tomasz Stanczak Jul 10 '12 at 12:07