Question

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

How to set progress view for this loop , i want to set progress view for downloading data. as well as progress label (i.e. %) i want percentage decimals.

Was it helpful?

Solution

The simple solution is to do this asynchronously, updating the progress view as you go:

  1. Create the progress view and add it to your view

  2. Dispatch your code to a background queue

  3. As each download finishes, dispatch the update of the progress view back to the main queue

In pseudo code, that would look like

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

There are a whole series of increasingly elegant approaches, too:

  1. Use concurrent queue (rather than the above, which downloads the images serially) to download the images, which will be significantly faster. I might suggest operation queue with maxConcurrentCount of 5, to enjoy concurrency, but make sure you don't exceed the iOS limit in the number of concurrent requests.

  2. Use NSURLConnectionDataDelegate based download rather than the NSData method initWithContentsOfURL, which can offer interim progress during the individual downloads. See download manager or download operation for examples.

  3. Use AFNetworking which also provides download progress block-based interface.


Above, in point 1, I suggested you consider using a concurrent queue, so I decided to benchmark it. For me, this GCD implementation below was 3-4 times slower than the NSOperationQueue implementation which follows it.

Here is GCD implementation:

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

to this NSOperationQueue implementation:

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

Bottom line, if you use NSOperationQueue (which not only provides concurrency, which you can also do in a GCD concurrent queue, but it also lets you easily control the number of concurrent operations (which you should limit to five or less for network operations)), you'll enjoy a significant performance benefit.

Even better, as I suggested, would be to employ AFNetworking, in which you enjoy not only this operation queue concurrency benefit, both other benefits, too.

OTHER TIPS

[progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
self.progressLabel.text = [NSString stringWithFormat:@"%.0f",self.progressView.progress*100];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top