2

I have four urls which consists images... I'm downloding those images and placing them into documents folder..

here is my code..

-(void)viewDidLoad
{
NSMutableArray *myUrlsArray=[[NSMutableArray alloc]init];
[myUrlsArray addObject:@"http://blogs.sfweekly.com/thesnitch/steve_jobs3.jpg"];
[myUrlsArray addObject:@"http://www.droid-life.com/wp-content/uploads/2012/12/Steve-Jobs-Apple.jpg"];
[myUrlsArray addObject:@"http://2.bp.blogspot.com/-T6nbl0rQoME/To0X5FccuCI/AAAAAAAAEZQ/ipUU7JfEzTs/s1600/steve-jobs-in-time-magazine-front-cover.png"];
[myUrlsArray addObject:@"http://images.businessweek.com/ss/08/09/0929_most_influential/image/steve_jobs.jpg"];
[myUrlsArray addObject:@"http://cdn.ndtv.com/tech/gadget/image/steve-jobs-face.jpg"];

for (int i=0; i<myUrlsArray.count; i++)
{
    [self downloadImageFromURL:[myUrlsArray objectAtIndex:i] withName:[NSString stringWithFormat:@"MyImage%i.jpeg",i]];
}

}




#pragma mark- downloading File

-(void)downloadImageFromURL:(NSString *)myURLString withName:(NSString *)fileName
{
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:myURLString]]];

NSLog(@"%f,%f",image.size.width,image.size.height);

   // Let's save the file into Document folder.**

    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];

NSString *jpegPath = [NSString stringWithFormat:@"%@/%@",documentsPath,fileName];// this path if you want save reference path in sqlite
NSData *data2 = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];//1.0f = 100% quality
[data2 writeToFile:jpegPath atomically:YES];

}

NOW... I need to display a UIProgressView for above downloading progress accurately.

how can i achieve this functionality...

Can any one provide some guidelines to achieve this..

Thanks in advance...

Ashok
  • 5,585
  • 5
  • 52
  • 80
  • 1
    For this thing at first you must know the total size of the image before it is downloaded entirely then you have to calculate the downloaded data every time and the percentage of that data w.r.t the total size then only I think it is possible. – Exploring Mar 15 '13 at 13:48
  • 1
    BTW, you really don't want to download the `NSData`, create a `UIImage`, and then re-extract the `NSData` using `UIImageJPEGRepresentation`. JPG is lossy so you're either going to lose quality, or if you use 1.0 for quality, the file will undoubtedly be bigger than the original. If you do this yourself, make sure to retrieve the original `NSData` into its own variable, and when you want to save it save that, not the result of `UIImageJPEGRepresentation`. – Rob Mar 15 '13 at 13:53
  • 1
    It's a hack so I won't list is as a answer.. But what I did in this case was to build my own progress bar and update it by 25% as each image is completed. Actually to make it look like it doesn't just stop at 75% and then disappear, I take it to like 90% then do a couple other quick tasks before removing it. – badweasel Mar 15 '13 at 13:57

4 Answers4

2

Your call to dataWithContentsOfURL is synchronous, meaning you don't get updates as the download is in process.

You can use a library like AFNetworking (https://github.com/AFNetworking/AFNetworking) which has callbacks to the progress of the download.

Richard Brown
  • 11,346
  • 4
  • 32
  • 43
2

Actually a better solution is to use SDWebImage manager which will load the images in the background for you and cache them. Then the next time you use that image it will check the cache. Google it.

That way the user also doesn't have to sit around and wait while you're downloading stuff..

Then look at this other question that has some ideas on how to do a status:

How to show an activity indicator in SDWebImage

Community
  • 1
  • 1
badweasel
  • 2,349
  • 1
  • 19
  • 31
2

Do not use dataWithContentsOfURL, you are blocking the main thread until the data arrives.

Instead create your own connection with NSURLConnection and start listening to your delegate.

  • connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response: get the total data size with [response expectedContentLength].
  • connection:(NSURLConnection *)connection didReceiveData:(NSData *)data: This is where you do your calculations and update your UIProgressView. Something like, loadedBytes/total data size.

Good luck.

Desdenova
  • 5,326
  • 8
  • 37
  • 45
2

I'd suggest you use some asynchronous downloading technique (either AFNetworking, SDWebImage, or roll your own with delegate-based NSURLSession) rather than dataWithContentsOfURL so that (a) you don't block the main queue; and (b) you can get progress updates as the downloads proceed.

I'd also suggest creating a NSProgress for each download. When your delegate method gets updates about how many bytes have been downloaded, update the NSProgress object.

You then can associate each NSProgress with a observedProgress for a UIProgressView, and when you update your NSProgress, the UI can be updated automatically.

Or, if you and a single UIProgressView to show the aggregate progress of all of the NSProgress for each download, you can create a parent NSProgress, establish each download's NSProgress as a child of the parent NSProgress, and then, as each download updates its respective NSProgress, this will automatically trigger the calculation of the parent NSProgress. And again, you can tie that parent NSProgress to a master UIProgressView, and you'll automatically update the UI with the total progress, just by having each download update its individual NSProgress.


There is a trick, though, insofar as some web services will not inform you of the number of bytes to be expected. They'll report an "expected number of bytes" of NSURLResponseUnknownLength, i.e. -1! (There are logical reasons why it does that which are probably beyond the scope of this question.) That obviously makes it hard to calculate what percentage has been downloaded.

In that case, there are a few approaches:

  1. You can throw up your hands and just use an indeterminate progress indicator;

  2. You can try changing the request such that web service will report meaningful "expected number of bytes" values (e.g. https://stackoverflow.com/a/22352294/1271826); or

  3. You can use an "estimated download size" to estimate the percentage completion. For example, if you know your images are, on average, 100kb each, you can do something like the following to update the NSProgress associated with a particular download:

    if (totalBytesExpectedToWrite >= totalBytesWritten) {
        self.progress.totalUnitCount = totalBytesExpectedToWrite;
    } else {
        if (totalBytesWritten <= 0) {
            self.progress.totalUnitCount = kDefaultImageSize;
        } else {
            double written = (double)totalBytesWritten;
            double percent = tanh(written / (double)kDefaultImageSize);
            self.progress.totalUnitCount = written / percent;
        }
    }
    
    self.progress.completedUnitCount = totalBytesWritten;
    

    This is a bit of sleight of hand that uses the tanh function to return a "percent complete" value that smoothly and asymptotically approaches 100%, using the kDefaultImageSize as the basis for the estimation.

    It's not perfect, but it yields a pretty decent proxy for percent completion.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Once again Thanks For spending your valuable time to provide a Good And Detailed answer for my Question...Your answer helped me a lot... – Ashok Mar 16 '13 at 06:48
  • hey @Rob.i am getting crash at [__NSCFString totalBytesExpected]: unrecognized selector sent to instance . – Krutarth Patel Mar 20 '18 at 05:09
  • That error is telling you that `totalBytesExpected` is being called on a `NSString` (the `__NSCFString` is a member of the `NSString` class cluster). Above, all of the `totalBytesExpected` references are being called on a `ImageDownload` instance. Somewhere you have accidentally passed a `NSString` object instead. (Did you do a cast somewhere?) Without knowing precisely where you called `totalBytesExpected` on a `NSString`, it's hard to diagnose where it went wrong. Do you know which line you're getting that error on? – Rob Mar 20 '18 at 05:27
  • @Rob,i am getting crash at if (imageDownload.totalBytesExpected >= 0) this line – Krutarth Patel Mar 20 '18 at 05:48
  • 1
    Set a breakpoint there and look at the `imageDownload` reference. If you're getting that particular error there, it must not be an `ImageDownload` reference, but rather a `NSString` for some reason. – Rob Mar 20 '18 at 05:54
  • @Rob,i have one question,loader is running like forward and backward progress,sometime it works smooth,dont know why this happening.can you please help to figure out this issue. – Krutarth Patel Mar 20 '18 at 13:37
  • 1
    @KrutarthPatel - That can happen if your server is returning `NSURLResponseUnknownLength`, ie. -1, for the `totalBytesExpected`. And the rendition of the calculating percent complete that I included in my original answer introduced some discontinuities in the percent completion in that scenario. Since I wrote that answer in 2013, I've since adopted a new method for estimating the percent completion in those cases where the exact size of the download is unknown, but for which you have reasonable guess as to the size, namely `tanh` as shown in my revised answer above. – Rob Mar 20 '18 at 22:46
  • @Rob how can get totalBytesExpectedToWrite and totalBytesWritten,sorry i am questioning more – Krutarth Patel Mar 21 '18 at 05:51
  • @Rob Sir,i post question https://stackoverflow.com/questions/49399784/image-downloading-progress-bar-not-smooth-using-afnetworking-in-ios,can you please check ? – Krutarth Patel Mar 21 '18 at 06:39