Сбой приложения MBProgress HUD при перезагрузке данных в tableView

Я использую этот фрагмент кода для отображения MBProgressHUD поверх одного из моих представлений, пока я загружаю данные из веб-службы, единственная проблема заключается в том, что иногда этот код приводит к зависанию приложения, ничего не делая, пока отображается HUD. "Скачивается" и экран блокируется. Кроме того, если у меня есть что-то вроде клавиатуры, отображаемой пользователю, когда я нажимаю кнопку обновления (кнопка обновления выполняет загрузку), то приложение падает в строке:

 [self.tableView reloadData];

Мой код:

//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);
}

Любая помощь приветствуется.

разъем


person Jack Nutkins    schedule 10.07.2012    source источник


Ответы (3)


pullAndDisplayData выполняется в отдельном потоке. Это сделано для того, чтобы MBProgressHUD мог использовать поток пользовательского интерфейса для отображения себя. Вы всегда должны обновлять свой пользовательский интерфейс из основного (UI) потока. Используйте метод performSelectorOnMainThread: для вызова [self.tableView reloadData]; и других элементов пользовательского интерфейса. Я предполагаю, что [downloader pullAndDisplayData]; является синхронным вызовом.

person msk    schedule 10.07.2012

Из 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;

Поскольку вы используете этот метод, ваш pullAndDisplayData выполняется в новом потоке. Это может вызвать странную проблему, которая у вас есть (я полагаю). Вы обновляете элементы пользовательского интерфейса из фонового потока, и это нехорошо. Элементы пользовательского интерфейса будут обновляться из основного потока. Используйте фоновый поток только для загрузки данных.

Вместо этого попробуйте использовать 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...
    });
});

Дополнительную информацию см. в разделе Использование файла MBProgressHUD.

person Lorenzo B    schedule 10.07.2012

Проблема может возникнуть из-за проблем с памятью. Попробуйте использовать для этого SVProgressHUD, это расширенная версия MBProgressHUD:

Вам просто нужно сделать так:

- (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];
}

Не нужно ничего выделять или освобождать. Работает именно так!!!

Всего наилучшего!!!

person Kanan Vora    schedule 10.07.2012
comment
Поскольку я не могу распознать фоновый поток/очередь где-либо здесь в вашем примере, похоже, что он будет работать в основном потоке и блокировать его, таким образом, не показывая прогресс, поскольку у пользовательского интерфейса не будет возможности запустить и обновить HUD. - person Tomasz Stanczak; 10.07.2012
comment
Это был не мой вопрос, я просто комментирую ваш код, а не использую его. Если вы посмотрите на вопрос, вы увидите, что его целью было запустить некоторый код в фоновом режиме, одновременно отображая HUD. - person Tomasz Stanczak; 10.07.2012