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

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

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

2 ответа

Решение

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

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

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

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

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

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, но также позволяет легко контролировать количество одновременных операций (которое вы должны ограничить пятью или менее для сетевых операций)), вы получите значительный выигрыш в производительности.

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

[progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
self.progressLabel.text = [NSString stringWithFormat:@"%.0f",self.progressView.progress*100];
Другие вопросы по тегам