Установить представление прогресса в цикле или обновить представление прогресса
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 ответа
Простое решение состоит в том, чтобы сделать это асинхронно, обновляя представление хода выполнения по мере продвижения:
Создайте вид прогресса и добавьте его в свой вид
Отправьте ваш код в фоновую очередь
По завершении каждой загрузки отправьте обновление просмотра хода выполнения обратно в основную очередь.
В псевдокоде это будет выглядеть
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];
});
}
});
Существует также целый ряд все более элегантных подходов:
Используйте параллельную очередь (а не описанную выше, которая загружает изображения последовательно) для загрузки изображений, что будет значительно быстрее. Я мог бы предложить очередь работы с
maxConcurrentCount
из5
, чтобы наслаждаться одновременностью, но убедитесь, что вы не превышаете лимит iOS в количестве одновременных запросов.использование
NSURLConnectionDataDelegate
на основе загрузки, а неNSData
методinitWithContentsOfURL
, который может предложить промежуточный прогресс во время отдельных загрузок. См. Менеджер загрузки или операцию загрузки для примеров.Используйте 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];