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:
- https://github.com/Alamofire/AlamofireImage
- https://github.com/onevcat/Kingfisher
- https://github.com/rs/SDWebImage
- 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:
display some placeholder image in your table cell while the image is being downloaded (otherwise your table will look empty);
when the downloaded image is there, send a refresh message to your table.
For 2, you have two approaches:
easy one: send
reloadData
to your table view (and check performance of your app);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:
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];
}
}
}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:
do not load the image in
parser:foundCDATA:
, just store the image URL;start downloading the image in
tableView:cellForRowAtIndexPath:
(if you know the URL, you can use dataWithContentOfURL as you are doing on a separate thread);the code I posted above will make the table update when the image is there;
at first, do not worry about scrolling/dragging, just make 1-2-3 work;
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
Drawing a Polygon with One Color for Stroke, and a Different One for Fill
Supportedinterfaceorientations Not Working
iOS App Getting Throttled from Local Searches
iOS 13 Set Uisearchtextfield Placeholder Color
Create Directory in App Group Container Swift
How to Localize Label Strings in iOS for a Beginner
Is Float, Double, Int an Anyobject
Recording from Remoteio: Resulting .Caf Is Pitch Shifted Slower + Distorted
How to Unit Test an App Extension on Xcode 6
How to Detect Orientation in an App Extension
How to Load Multiple Storyboard Files Depending on iOS Version? (5 and 6)
All Notifications Disappearing After Opening One of Them
How Could You Make a Uilabel Wrap Around an Image (Like Shown)
Create a Tabbar Controller with a Master-Detail Template
Nsindexpath? Does Not Have a Member Name 'Row' Error in Swift