In UITableviewcell I have many Image view which will load the image from url
I want to store the image in cache so that when I scroll again it should not load from url
My Code is
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[indicator startAnimating];
[indicator setCenter:cell.image_VW.center];
[cell.contentView addSubview:indicator];
NSString *url_STR = [NSString stringWithFormat:#"%#news/%#",IMAGE_URL,[tmp_NEWS valueForKey:#"image"]];
NSString *url_STR = [NSString stringWithFormat:#"%#news/%#",IMAGE_URL,[tmp_NEWS valueForKey:#"image"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//retrive image on global queue
UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url_STR]]];
dispatch_async(dispatch_get_main_queue(), ^{
[indicator removeFromSuperview];
NEWS_BIG_CELL * cell = (NEWS_BIG_CELL *)[self.tbl_CONTENTS cellForRowAtIndexPath:indexPath];
//assign cell image on main thread
//cell.image_VW.contentMode = UIViewContentModeScaleAspectFit;
cell.image_VW.image = img;
});
});
you should use NSCache, where you can save you images by keys and get them if they are exist in cache (by key), please see this swift example :
http://www.splinter.com.au/2015/09/24/swift-image-cache/
or use:
https://github.com/rs/SDWebImage
For Simple solution is SDWebImage for caching image gitHub link
in your UITableviewcell cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
[cell.image_VW sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
First you need to check if the data is not nil from dataWithContentsOfURL, then if the check is true:
you can save the image as NSData in NSUserdefaults with its URL as the key (setObject:(NSData) forKey:(URL as NSString)) of that preference, so that next time, you can check in the cache if any data exists for the URL to display it immediately without getting it again from the internet (objectForKey:(URL as NSString)).
Related
im making a tableview loaded with some NSArrays, the cell contains two labels and a background image view loaded with a URL image. The problem is that the scrolling of the tableview is slow, its like freeze or something.... i think that is because of the Imageview but what can i do???
heres some of my code so you can see:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _Title.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _content[row];
cell.CellImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]]];
cell.CellImageView.contentMode = UIViewContentModeTop;
cell.CellImageView.contentMode = UIContentSizeCategoryMedium;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
For extra info this tableview presents a Detailviewcontroller with this same info.
Load the image asynchronously(load it as the images come in). Look into great classes such as SDWebImage https://github.com/rs/SDWebImage
You are blocking the main thread with the following line of code:
cell.CellImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]]];
My suggestion would be to use AFNetworking and replace the code with the following:
[cell.cellImageView setImageWithURL:[NSURL URLWithString:#"http://example.com/image.png"]];
Also, your pointers should start with a lower case letter. For instance, CellImageView should be cellImageView.
You are calling [NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]] from cellForRowAtIndexPath , which is not good idea. Because it'll go to the server every time a UITableViewCell is loaded.
You must use Asynchronous image loading & cache. These libraries might help you : (My personal favourite is SDWebImage)
1) https://github.com/rs/SDWebImage
2) https://github.com/nicklockwood/AsyncImageView
And more, you can refer to the sample code by Apple about LazyTableImages - https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html
UPDATE:
For SDWebImage follow this guide. It's very good.
http://iosmadesimple.blogspot.in/2013/04/lazy-image-loading.html
To expand on #vborra's answer - the reason why your tableview is slow is that (in your code) the entire image MUST have finished downloading before it displays.
This is because dataWithContentsOfURL: is a synchronous method. You need to use asynchronous APIs to download images in the background and when they've finished downloading, display them on the screen.
Here is a code snippet from the github page adapted for your example. Make sure you add the folder SDWebImage and it's contents from https://github.com/rs/SDWebImage to your project.
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _content[row];
[cell.CellImageView setImageWithURL:[NSURL URLWithString:_ImageUrl[row]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.CellImageView.contentMode = UIViewContentModeTop;
cell.CellImageView.contentMode = UIContentSizeCategoryMedium;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
Note, if you're downloading images of all different sizes, you may end up with resizing issues which will also decrease your performance. The SDWebImage page has a link to an article to help you with this problem if you come across it. http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/
You may also experience a performance bump when using transparency and tableviews, but it depends on the rest of your code.
AFNetworking is an excellent tool to use, but might be overkill if you're not using networking anywhere else in your app.
load your image like this
//get a dispatch queue
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:mapUrlStr]];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
cell.CellImageView.image = [UIImage imageWithData:imageData]
});
});
I have a table view that loads title and images from url address. Since I added the images, the scrolling isn't smooth. I have changed the background to clear. The images are low resolution. I prefer accessing them with url, not to downloaded the image and re-upload it on the app. Looking forward for your solutions to make the scrolling smooth. Thanks
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TableCell" forIndexPath:indexPath];
NSString *str = [[feeds objectAtIndex:indexPath.row] objectForKey: #"title"];
[[cell textLabel] setNumberOfLines:0]; // unlimited number of lines
[[cell textLabel] setFont:[UIFont systemFontOfSize: 16.0]];
cell.backgroundColor = [UIColor clearColor];
cell.contentView.backgroundColor = [UIColor clearColor];
cell.TitleLabel.text=str;
UIImage *pImage=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:feeds2[indexPath.row]]]];;
[cell.ThumbImage setImage:pImage];
return cell;
}
replace your code in cellForRowAtIndexPath:
after this line cell.TitleLabel.text=str;
That way you load each image in the background and as soon as its loaded the corresponding cell is updated on the mainThread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
NSData *imgData = NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:feeds2[indexPath.row]]];
if (imgData) {
UIImage *image = [UIImage imageWithData:imgData];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
UIImage *image = [UIImage imageWithData:imgData];
if (image) {
cell.ThumbImage.image = image;
}
});
});
A better approach is to cache the image , so you dont need to download them each time , the table scroll.
here are some very good references to accomplish this.
LazyTableImages Reference
SDWebImage
UIImageView+AFNetworking
Answer is simply you have to implement the loading with NSOperation where a custom class to handle your download and have your NSOperationQueue as downloadQueue. Every UITableView is a (sub class) UIScrollView therefore you can use the methods directly.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[downloadQueue cancelAllOperations]; // clear your queue
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
// start download only for visible cells
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// start download only for visible cells
}
for more detail information visit this tutorial. There you can really find good solution for your need.
In my app i have to fetch images from a json webservice and display them in a table view. While scrolling downwards all the images come correct order but while i revert backwards all the images get over each other. I am using [ tableview reloaddata] then too its happening.
This happened to me in my app, where I have a leaderboards table. It is definitely because the tables are being reused.
The easiest way to fix this is simply setting the cell's image to nil first, then downloading and displaying the new image.
There are definitely other ways to do this, but I can definitely vouch for this one, as I use it in my own app, and it works great!!
Here's how I solve the problem.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * cellIdentifier = #"Cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
// Make sure you set the image for the cell to nil first
[cell.imageView setImage:nil];
// Then load your new image using the url and display it
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.imagedownloadqueue", NULL);
dispatch_async(backgroundQueue, ^(void){
NSUrl * url = [NSUrl UrlWithString:#"www.example.com"]; // Url of image to download
UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfUrl:url]];
if (image)
{
dispatch_async(dispatch_get_main_queue(), ^(void) {
[cell.imageView setImage:image]; // Display image after it is downloaded
});
}
});
return cell;
}
im making a tableview loaded with some NSArrays, the cell contains two labels and a background image view loaded with a URL image. The problem is that the scrolling of the tableview is slow, its like freeze or something.... i think that is because of the Imageview but what can i do???
heres some of my code so you can see:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _Title.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _content[row];
cell.CellImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]]];
cell.CellImageView.contentMode = UIViewContentModeTop;
cell.CellImageView.contentMode = UIContentSizeCategoryMedium;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
For extra info this tableview presents a Detailviewcontroller with this same info.
Load the image asynchronously(load it as the images come in). Look into great classes such as SDWebImage https://github.com/rs/SDWebImage
You are blocking the main thread with the following line of code:
cell.CellImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]]];
My suggestion would be to use AFNetworking and replace the code with the following:
[cell.cellImageView setImageWithURL:[NSURL URLWithString:#"http://example.com/image.png"]];
Also, your pointers should start with a lower case letter. For instance, CellImageView should be cellImageView.
You are calling [NSData dataWithContentsOfURL:[NSURL URLWithString:_ImageUrl[row]]] from cellForRowAtIndexPath , which is not good idea. Because it'll go to the server every time a UITableViewCell is loaded.
You must use Asynchronous image loading & cache. These libraries might help you : (My personal favourite is SDWebImage)
1) https://github.com/rs/SDWebImage
2) https://github.com/nicklockwood/AsyncImageView
And more, you can refer to the sample code by Apple about LazyTableImages - https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html
UPDATE:
For SDWebImage follow this guide. It's very good.
http://iosmadesimple.blogspot.in/2013/04/lazy-image-loading.html
To expand on #vborra's answer - the reason why your tableview is slow is that (in your code) the entire image MUST have finished downloading before it displays.
This is because dataWithContentsOfURL: is a synchronous method. You need to use asynchronous APIs to download images in the background and when they've finished downloading, display them on the screen.
Here is a code snippet from the github page adapted for your example. Make sure you add the folder SDWebImage and it's contents from https://github.com/rs/SDWebImage to your project.
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
int row = [indexPath row];
cell.TitleLabel.text = _Title[row];
cell.DescriptionLabel.text = _content[row];
[cell.CellImageView setImageWithURL:[NSURL URLWithString:_ImageUrl[row]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.CellImageView.contentMode = UIViewContentModeTop;
cell.CellImageView.contentMode = UIContentSizeCategoryMedium;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
Note, if you're downloading images of all different sizes, you may end up with resizing issues which will also decrease your performance. The SDWebImage page has a link to an article to help you with this problem if you come across it. http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/
You may also experience a performance bump when using transparency and tableviews, but it depends on the rest of your code.
AFNetworking is an excellent tool to use, but might be overkill if you're not using networking anywhere else in your app.
load your image like this
//get a dispatch queue
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:mapUrlStr]];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
cell.CellImageView.image = [UIImage imageWithData:imageData]
});
});
I have am downloading multiple images from a web server at the same time, and I the problem is they are getting mixed up with each other.
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:#"website.com"]];
if (data == nil)
return;
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:data];
});
});
I got this code from: Getting Image from URL Objective C.
How do i fix this?? also is there any way to speed up download speeds?
You can use "AsyncImageView" class files it will load image synchronously and it shows the activity indicator while image loading
AsyncImageView is the class file in which it will create connection for each call and when image data downloading completed it will return image for imageview. and if image is already in cache then just return image without creating connection.
You can download "AsyncImageView" class files from following link:- https://www.dropbox.com/s/peazwjvky9fsjd7/Archive.zip
in .m file import AsyncImageView Class
#import "AsyncImageView.h"
in your tableview cell at indexpath method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(X, y, width, height)];
NSString *imageURL = [NSString stringWithFormat: #"www.xyz.image.png"];
AsyncImageView *async = [[AsyncImageView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
[async loadImageFromURL:[NSURL URLWithString:imageURL]];
[imageView addSubview:async];
[cell addSubview:imageView];
return cell;
}
try this your problem will solve.