Установите представление о прогрессе в цикле или обновление представления прогресса

StackOverflow https://stackoverflow.com/questions/19834016

  •  25-07-2022
  •  | 
  •  

Вопрос

for (int i=0; i<[array count]; i++)
{
    NSError *error;
    NSArray *ipaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *idocumentsDir = [ipaths objectAtIndex:0];
    NSString *idataPath = [idocumentsDir stringByAppendingPathComponent:@"File"];
    NSLog(@"idataPath:%@",idataPath);

    //Create folder here
    if (![[NSFileManager defaultManager] fileExistsAtPath:idataPath])
    {
        [[NSFileManager defaultManager] createDirectoryAtPath:idataPath withIntermediateDirectories:NO attributes:nil error:&error];
    }
    // Image Download here
    NSString *fileName = [idataPath stringByAppendingFormat:@".jpg"];
    NSLog(@"imagePathDOWNLOAD:%@",fileName);

    _imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[array objectAtIndex:i]]];
    [_imgData writeToFile:fileName atomically:YES];

    tempImg.image = [UIImage imageWithData:_imgData];   
}

Как установить представление о прогрессе для этого цикла, я хочу установить представление о прогрессе для загрузки данных. Помимо лейбла Progress (т. Е. %) Я хочу процентных десятичных декораций.

Это было полезно?

Решение

Простое решение состоит в том, чтобы сделать это асинхронно, обновляя представление о прогрессе по мере продвижения:

  1. Создайте представление о прогрессе и добавьте его в свой вид

  2. Отправьте свой код на фоновую очередь

  3. По мере того, как каждая загрузка заканчивается, отправьте обновление представления Progress обратно в основную очередь

В псевдо -коде это будет выглядеть

UIProgressView *progressView = [[UIProgressView alloc] init];
// configure the progress view and add it to your UI

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i=0; i<[array count]; i++)
    {
        NSError *error;
        NSArray *ipaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *idocumentsDir = [ipaths objectAtIndex:0];
        NSString *idataPath = [idocumentsDir stringByAppendingPathComponent:@"File"];
        NSLog(@"idataPath:%@",idataPath);

        //Create folder here
        if (![[NSFileManager defaultManager] fileExistsAtPath:idataPath])
        {
            [[NSFileManager defaultManager] createDirectoryAtPath:idataPath withIntermediateDirectories:NO attributes:nil error:&error];
        }
        // Image Download here
        NSString *fileName = [idataPath stringByAppendingFormat:@".jpg"];
        NSLog(@"imagePathDOWNLOAD:%@",fileName);

        _imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[array objectAtIndex:i]]];
        [_imgData writeToFile:fileName atomically:YES];

        // now dispatch any UI updates back to the main queue
        dispatch_async(dispatch_get_main_queue(), ^{

            [progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
            tempImg.image = [UIImage imageWithData:_imgData];
        });
    }
});

Есть и целая серия все более элегантных подходов:

  1. Используйте одновременную очередь (а не приведенную выше, которая загружает изображения последовательно) для загрузки изображений, что будет значительно быстрее. Я мог бы предложить операцию очереди с maxConcurrentCount из 5, чтобы насладиться параллелизмом, но убедитесь, что вы не превышаете ограничение iOS в количестве параллельных запросов.

  2. Использовать NSURLConnectionDataDelegate На основе загрузки, а не NSData метод initWithContentsOfURL, который может предложить временный прогресс во время отдельных загрузок. Видеть менеджер загрузки или же Загрузка операции Например.

  3. Использовать Afnetworking который также предоставляет интерфейс на основе блоков на основе загрузки.


Выше, в точке 1, я предложил вам рассмотреть возможность использования параллельной очереди, поэтому я решил сравнить ее. Для меня эта реализация GCD ниже была в 3-4 раза медленнее, чем NSOperationQueue реализация, которая следует за этим.

Вот реализация GCD:

CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

UIProgressView *progressView = [self addProgressView];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSInteger downloadSuccessCount = 0;
    NSInteger downloadFailureCount = 0;

    NSString *idataPath = [self createDownloadPath];

    for (int i = 0; i < [array count]; i++)
    {
        // Image Download here
        NSString *filename = [self pathForItem:i array:array folder:idataPath];
        NSURL *url = [self urlForItem:i array:array];
        NSData *data = [[NSData alloc] initWithContentsOfURL:url];
        UIImage *image = nil;
        if (data)
            image = [UIImage imageWithData:data];
        if (image) {
            downloadSuccessCount++;
            [data writeToFile:filename atomically:YES];
        } else {
            downloadFailureCount++;
        }

        // now dispatch any UI updates back to the main queue
        dispatch_async(dispatch_get_main_queue(), ^{

            [progressView setProgress: (CGFloat) (downloadSuccessCount + downloadFailureCount) / [array count] animated:YES];

            // update the image in the UI if you want

            [UIView transitionWithView:self.imageView duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
                tempImg.image = image;
            } completion:nil];
        });
    }

    NSLog(@"Completed in %.1f seconds", CFAbsoluteTimeGetCurrent() - start);
});

к этому NSOperationQueue реализация:

CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

UIProgressView *progressView = [self addProgressView];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 5;

NSString *idataPath = [self createDownloadPath];
self.downloadSuccessCount = 0;
self.downloadFailureCount = 0;

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Completed in %.1f seconds", CFAbsoluteTimeGetCurrent() - start);
}];

for (int i = 0; i < [array count]; i++)
{
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        // Image Download here
        NSString *filename = [self pathForItem:i array:array folder:idataPath];
        NSURL *url = [self urlForItem:i array:array];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = nil;
        if (data)
            image = [UIImage imageWithData:data];
        if (image)
            [data writeToFile:filename atomically:YES];

        // now dispatch any UI updates back to the main queue
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            if (image) {
                self.downloadSuccessCount++;

                // update the image in the UI if you want, though this slows it down

                [UIView transitionWithView:self.imageView duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
                    tempImg.image = image;
                } completion:nil];
            }
            else
                self.downloadFailureCount++;

            [progressView setProgress: (CGFloat) (self.downloadSuccessCount + self.downloadFailureCount) / [array count] animated:YES];
        }];
    }];

    [queue addOperation:operation];
    [completionOperation addDependency:operation];
}

[queue addOperation:completionOperation];

Итог, если вы используете NSOperationQueue (Что не только обеспечивает параллелизм, которую вы также можете сделать в одновременной очереди GCD, но и позволяет легко контролировать количество одновременных операций (которые вы должны ограничивать пять или менее для сетевых операций), вам понравится Значительная выгода от производительности.

Еще лучше, как я предположил, было бы использовать афнов, в которой вы наслаждаетесь не только этой согласованной выгодой операции, а также и другими преимуществами.

Другие советы

[progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
self.progressLabel.text = [NSString stringWithFormat:@"%.0f",self.progressView.progress*100];
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top