iOS Lazy-Loading of Table Images

how to implement lazy loading of images in table view using swift

Old Solution:

Since you doesn't show any code.

Here is the example for you.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

// try to reuse cell
let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("DealCell") as CustomCell

// get the deal image
let currentImage = deals[indexPath.row].imageID
let unwrappedImage = currentImage
var image = self.imageCache[unwrappedImage]
let imageUrl = NSURL(string: "http://staging.api.cheapeat.com.au/deals/\(unwrappedImage)/photo")

// reset reused cell image to placeholder
cell.dealImage.image = UIImage(named: "placeholder")

// async image
if image == nil {

let request: NSURLRequest = NSURLRequest(URL: imageUrl!)

NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
if error == nil {

image = UIImage(data: data)

self.imageCache[unwrappedImage] = image
dispatch_async(dispatch_get_main_queue(), {
cell.dealImage.image = image

})
}
else {

}
})
}

else{
cell.dealImage.image = image
}

return cell

}

Follow THIS tutorial for more Info. Hope this will help you.

New Solution:

Here is extension for it which is created by my friend Leo Dabus which is really simple to use:

extension UIImageView {
func downloadImageFrom(link link:String, contentMode: UIViewContentMode) {
NSURLSession.sharedSession().dataTaskWithURL( NSURL(string:link)!, completionHandler: {
(data, response, error) -> Void in
dispatch_async(dispatch_get_main_queue()) {
self.contentMode = contentMode
if let data = data { self.image = UIImage(data: data) }
}
}).resume()
}
}

Now in your cellForRowAtIndexPath method assign image to cell this way:

cell.cellImageView.image = UIImage(named: "placeholder")  //set placeholder image first.
cell.cellImageView.downloadImageFrom(link: imageLinkArray[indexPath.row], contentMode: UIViewContentMode.ScaleAspectFit) //set your image from link array.

And as Rob suggested into comment here is some useful libraries which you can use:

  1. https://github.com/Alamofire/AlamofireImage
  2. https://github.com/onevcat/Kingfisher
  3. https://github.com/rs/SDWebImage
  4. https://github.com/kean/DFImageManager

iOS lazy-loading of table images

You can achieve lazy loading in your tableview by following these steps :

In your Class.h, put:

NSMutableDictionary *Dict_name;
BOOL isDragging_msg, isDecliring_msg;

Now In Class.m file, put this code in view did load:

Dict_name = [[NSMutableDictionary alloc] init];

In cellForRowAtIndexPath:

if ([dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]]) { 
cell.image_profile.image=[dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
}
else
{
if (!isDragging_msg && !isDecliring_msg)
{
[dicImages_msg setObject:[UIImage imageNamed:@"Placeholder.png"] forKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
[self performSelectorInBackground:@selector(downloadImage_3:) withObject:indexPath];
}
else
{
cell.image_profile.image=[UIImage imageNamed:@"Placeholder.png"];
}
}

And for the download image the function is:

-(void)downloadImage_3:(NSIndexPath *)path{
NSAutoreleasePool *pl = [[NSAutoreleasePool alloc] init];

NSString *str=[here Your image link for download];

UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:str]]];

[dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:@"image name or image link same as cell for row"]];

[tableview performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

[pl release];
}

And at last put these methods in your class:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
isDragging_msg = FALSE;
[tableview reloadData];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
isDecliring_msg = FALSE;
[tableview reloadData];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
isDragging_msg = TRUE;
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
isDecliring_msg = TRUE;
}

UITableView On Screen Image Download Lazy Loading

I think what you have to do is:

  1. display some placeholder image in your table cell while the image is being downloaded (otherwise your table will look empty);

  2. when the downloaded image is there, send a refresh message to your table.

For 2, you have two approaches:

  1. easy one: send reloadData to your table view (and check performance of your app);

  2. send your table view the message:

    - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

Using reloadRowsAtIndexPaths is much better, but it will require you to keep track of which image is associated to which table row.

Keep in mind that if you use Core Data to store your images, then this workflow would be made much much easier by integrating NSFetchedResultController with your table view. See here for an example.

Again another approach would be using KVO:

  1. declare this observe method in ItemsViewCell:

    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context {

    if ([keyPath isEqual:@"thumbnail"]) {
    UIImage* newImage = [change objectForKey:NSKeyValueChangeNewKey];
    if (newImage != (id)[NSNull null]) {
    self.thumbContainer.image = newImage;
    [self.thumbContainer setNeedsLayout];
    }
    }

    }
  2. then, when you configure the cell do:

    RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];
    cell.titleLabel.text = [item title];
    cell.thumbContainer.image = [item thumbnail];

    [item addObserver:cell forKeyPath:@"thumbnail" options:NSKeyValueObservingOptionNew context:NULL];

By doing this, cell will be notified whenever the given item "thumbnail" keypath changes.

Another necessary change is doing the assignment like this:

       self.thumbnail = [UIImage imageWithData:tempData];

(i.e., using self.).

ANOTHER EDIT:

I wanted to download and load the images just like in the LazyTableImages example by Apple. When its not decelerating and dragging, then only onscreen images are loaded, not all images are loaded at once.

I suspect we are talking different problems here.

I thought your issue here was that the downloaded images were not displayed correctly (if you do not scroll the table). This is what I understand from your question and my suggestion fixes that issue.

As to lazy loading, there is some kind of mismatch between what you are doing (downloading the whole feed and then archiving it as a whole) and what you would like (lazy loading). The two things do not match together, so you should rethink what you are doing.

Besides this, if you want lazy loading of images, you could follow these steps:

  1. do not load the image in parser:foundCDATA:, just store the image URL;

  2. start downloading the image in tableView:cellForRowAtIndexPath: (if you know the URL, you can use dataWithContentOfURL as you are doing on a separate thread);

  3. the code I posted above will make the table update when the image is there;

  4. at first, do not worry about scrolling/dragging, just make 1-2-3 work;

  5. once it works, use the scrolling/dragging delegate to prevent the image from being downloaded (point 2) during scrolling/dragging; you can add a flag to your table view and make tableView:cellForRowAtIndexPath: download the image only if the flag says "no scrolling/dragging".

I hope this is enough for you to get to the end result. I will not write code for this, since it is pretty trivial.

PS: if you lazy load the images, your feed will be stored on disk without the images; you could as well remove the CGD group and CGD wait. as I said, there is not way out of this: you cannot do lazy loading and at the same time archive the images with the feed (unless each time you get a new image you archive the whole feed). you should find another way to cache the images.

Image lazy loading in table view

Try this code :

   UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.frame = CGRectMake(your bounds);
[cell addSubview:indicator];
[indicator bringSubviewToFront:self.view];
[indicator startAnimating];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
//This is what you will load lazily
NSString *u=[NSString stringWithContentsOfFile:r.url encoding:NSUTF8StringEncoding error:nil];
NSURL *imageURL=url;
NSData *image=[NSData dataWithContentsOfURL:imageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image=[UIImage imageWithData:image];
[cell setNeedsLayout];
[indicator stopAnimating];

});
});
cell.cellDescription.text=r.url;// this is which you want to add in first time.

I am not sure but try it.

Lazy loading image to table view sections repeating

Your cells are dequeued and repeated in section 0 as per the screenshot.

you need to set the cell.imageView.image = nil; per cellForRowAtIndexPath call so that reused cell image clears up before you assign new image.

Load more images in UITableView with images using lazy loading

UITableView already does lazy loading for you. It will only ask for the visible cells. If you are taking about adding more images to the table while the user is scrolling, you can implement UIScrollViewDelegate methods to calculate the current scrolling position, and when the user reaches certain point, add more images to the NSMutableArray on your datasource.



Related Topics



Leave a reply



Submit