2

I have tableviewcontroller and data fecthed from the server. I use following class to download the data asyn. but my problem is data is loading when user sees the tableViewcontroller. I want data being loaded before user sees.

    #import <SDWebImage/UIImageView+WebCache.h>

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    cell.textLabel.text = [[tableData objectAtIndex:indexPath.row] valueForKey:@"name"];
    cell.textLabel.font = [UIFont fontWithName:@"BebasNeue" size:24];
    cell.textLabel.textColor = [UIColor whiteColor];


    NSString *imageURLString=[[tableData objectAtIndex:indexPath.row] valueForKey:@"logo"];
    NSString* imageURL = [[tableData objectAtIndex:indexPath.row] valueForKey:@"picture"];
    [cell.imageView setImageWithURL:[NSURL URLWithString:imageURL]];

}
  • You should check out SDWebImage's [`SDWebImagePrefetcher`](http://hackemist.com/SDWebImage/doc/Classes/SDWebImagePrefetcher.html) class, which is designed precisely for this sort of scenario. Just have your scroll view delegate methods perform the prefetch. That way, you enjoy the SDWebImage functionality, but with some prefetching. See http://stackoverflow.com/a/15710695/1271826 for an example. – Rob May 19 '14 at 16:20
  • BTW, when you're testing your code, I'd heartily encourage you to run the app in conjunction with the Network Link Conditioner, to make sure your final solution is well-suited for real-world network situations. Frankly, it's useful for diagnosing network code anyway (helps you identify bottlenecks, come up with a good balance between having images appear and not making the app too slow in order to accomplish that, etc.). – Rob May 20 '14 at 23:37

2 Answers2

0

Solution

With SDWebImage you can download the image first.

Where?

It depends on your implementation. Maybe in a previous controller, or in your appDelegate. If the tableview appears in your initial viewcontroller, chances are to absolutely depend on the network speed.

Example Code

Take a look at this code (extracted from the Readme of the library, check the source!):

Manager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL
                 options:0
                 progress:^(NSInteger receivedSize, NSInteger expectedSize)
                 {
                     // YOU DON'T NEED THIS
                 }
                 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
                 {
                     if (image)
                     {
                         // MAYBE YOU WANT TO DO SOMETHING HERE
                     }
                 }];

Then, in your tableView:cellForRowAtIndexPath: method, you can just set the image like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // ...
    NSURL* url = GET_IMAGE_URL;
    [cell.imageView setImageWithURL:url];
    // ...
}

How it works?

The library takes care of looking first in its cache, where it will find the previously downloaded image, because it uses the image urls as keys for the cache, so that the image appears inmediatly.

E-Riddie
  • 14,660
  • 7
  • 52
  • 74
sonxurxo
  • 5,648
  • 2
  • 23
  • 33
  • I'm not going to guess about the down-vote, but a comment on your answer. Rather than using `SDWebImageManager`, I'd recommend `SDWebImagePrefetcher` instead, which achieves the same thing, but the prefetch operations have a lower priority than requests from, for example, `setImageWithURL`, so SDWebImage will prioritize image views that are visible over the prefetching of random cells. You don't want a visible cell to get backlogged behind a bunch of requests for cells not yet visible. This is important in case user scrolls down quickly while the downloads are still in progress. – Rob May 20 '14 at 23:29
  • If I were going to refine this further, if the OP had a _really_ long list, you might want to constrain the prefetch to the next likely images, not the full set. (Apple will reject apps that unwittingly use up too much of a user's cellular data plan in too short of a time.) But, if the OPs table doesn't have too many rows, the distinction is academic. – Rob May 20 '14 at 23:32
  • @Rob `SDWebImagePrefetcher` internally uses `SDWebImageManager`, so I don't get the difference. – sonxurxo May 21 '14 at 07:26
-1

Issue

You are downloading data and loading it to the tableView async, that means your data process is executing on background, but the tableView is still active and it's shown.

My Solution

I handle this situation in two ways

  1. You first load all the data in a NSArray or NSDictionary (whatever object you need) before presenting your TableViewController, and pass it as an argument to the TableViewController. In other words, you need to load data before presenting the tableViewController.

  2. You need to create an animation view, or a loading view while you are performing the download of the data you tell the user that data is loading so wait until the process is done. And when the process is done, simply reload the tableView and pass the data to the TableViewCells.

Example

1) This code is used when you send request to server to get data (for example from JSON)

LoadingView *spinn = [LoadingView loadSpinnerIntoView:self.view];
dispatch_queue_t downloadQueue = dispatch_queue_create("LoadingView", NULL);
dispatch_async(downloadQueue, ^{

    // do our long running process here
    [NSThread sleepForTimeInterval:0.1];

    // do any UI stuff on the main UI thread
    dispatch_async(dispatch_get_main_queue(), ^{
        //Start downloading your data, and pass data you fetched to objects of NSDictionary

        //Your methods

        //After downloading data is done, reload data on tableView
        [tableView reloadData];

        //Remove loading view, and your tableViewController is all set
        [spinn removeSpinner];

    });});

2) After fetching data to NSDictionary or NSArray from the request you made, you have also links to download images.

For downloading image async without blocking mainThread you can refer to @sonxurxo question, or you can use this one which also uses SDWebImage

You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *MyIdentifier = @"MyIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:MyIdentifier] autorelease];
    }

    // Here we use the new provided setImageWithURL: method to load the web image
    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

    cell.textLabel.text = @"My Text";
    return cell;
}
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • Thanks a lot 3r1d, I would like to see little bit more how you handle NSDictionary if you have an example to share. –  May 19 '14 at 16:16
  • @3r1d I did not down-vote, but since you ask, the idea of showing the user a spinner while the app downloads all of the images is, generally, a really bad idea. Loading them into a dictionary is an even worse idea. It works in certain very simple scenarios, but in general, it's not scalable and suffers from a pretty bad idea UX in real-world environments. – Rob May 20 '14 at 03:33
  • Look this way I told him about wasn't about the images, was about getting the data from internet (for example imagelinks, name of an object, etc). For downloading images async, you just need to use imageCache SDWebImage is a great library, you can follow the sonxurxo's example or can see my answer here http://stackoverflow.com/a/23226364/2664437. If that's the point I guess I missunderstood the question he asked @Rob. Ill update answer if its needed – E-Riddie May 20 '14 at 07:28
  • This is great that you clarified that you didn't mean to suggest that you download all of the images into a dictionary/array first. That's how I read (misread?) your original answer, and I bet the person who down-voted did, too. This revised answer remedies that misconception, but I don't see how this addresses the OP's question of how to avoid the delay in the appearance of the images in the table. Your image retrieval routine, as I read it, is virtually identical to the OP's original code. Notably, the OP is already using SDWebImage. – Rob May 20 '14 at 23:30
  • @Rob thnx for your revision, I may be wrong, I don't know, but there's not a good way to download images into objects and pass their references to the tableView. This is not an efficient way. But the SDWebImage, also cache the images or you can store them to memory so you can get them back to the tableViewCells. So what should I do? Update the answer and say that's not an efficient mode? – E-Riddie May 21 '14 at 09:45