lazy loading images in tableview - ios

My problem is ---- I am using UITableView in custom cell when load data in tableview it is loading description and title also date but not load images in tableview.some times load all images in one row. When i am using this code scroll is working good but not load images in TableView. I want lazy load concepts. go to below link.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
newsobject=(newscustamCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (newsobject ==nil)
{
[[NSBundle mainBundle]loadNibNamed:#"newscustamCell" owner:self options:nil];
}
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(q, ^{
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"%#",[[newsarry objectAtIndex:indexPath.row] objectForKey:#"image_file"]]];
/* Fetch the image from the server... */
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
/* This is the main thread again, where we set the tableView's image to
be what we just fetched. */
newsobject.allnewsimg.image = img;
// newsobject.allnewsimg.image=[UIImage imageWithData:data];
});
});
newsobject.allnewsdes.text=[[[newsarry objectAtIndex:indexPath.row] objectForKey:#"long_desc"]stringByStrippingTags];
newsobject.allnewslabel.text=[[newsarry objectAtIndex:indexPath.row] objectForKey:#"title"];
![enter image description here][1]
newsobject.timelabel.text=[[newsarry objectAtIndex:indexPath.row] objectForKey:#"date"];
return newsobject;
}

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"%#",[[newsarry objectAtIndex:indexPath.row] objectForKey:#"image_file"]]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[tableView cellForRowAtIndexPath:indexPath].imageView.image = image;
});
});

You can use some Lib like SDWebImage or Afnetworking. Libs support lazy load image and auto cache this image.
All you need to do:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"url of image"]
placeholderImage:nil];

I will share you some code. Please follow this code.
First you have to retrieve all data from server. After that load on tableview.
Try this code :
- (void)viewDidLoad
{
NSString *urlString=#"http://put your url";
self.responseData=[NSMutableData data];
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] delegate:self];
NSLog(#"connection====%#",connection);
}
 JSON delegate methods -------------
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.responseData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.responseData=nil;
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSArray *response_Array = [NSJSONSerialization JSONObjectWithData:self.responseRankData options:kNilOptions error:nil];
NSDictionary *dataDictionary=nil;
NSDictionary *imgurlDic=nil;
NSArray *imgDic=nil;
// I am retrieve image according to my url. So please edit this according to your url. i am just write the format.
for (int i=0; i < response_Array.count; i++)
{
imgurlDic = [imgDic objectAtIndex:i];
imgDic = [dataDictionary objectForKey:#"imageKey"]; // Take your Key according to URL
NSURL *url = [NSURL URLWithString:[imgurlDic objectForKey:#"url"]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
[self.Img_Array addObject:img]; // Adding all image in your image Array.
NSLog(#"imgurlDic == %#",imgurlDic);
}
[self.tableview reloadData]; // reload your tableview here.
}
Getting all image in your array you can use this array in your cellForRowAtIndexPath method or where you want to load the images.

Because of the way that table view cells are quickly queued/reused, by the time that your network request finishes loading the cell you are referring to might not be the same one you think it is (because it went offscreen and got queued and then reassigned to a different indexPath). Also, before returning the cell you need to make sure you reset its imageView’s image property to nil or some default value. See my answer here for more info: https://stackoverflow.com/a/23473911/91458
Also - it’s a good idea to keep a cache of the images you already retrieved. Check the NSCache class, which is very similar to using a mutable dictionary but with built in memory management, etc.

I also used that trick, but the threads and the indexes between threads make a mess so I searched and I found a great library on GitHub.
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 *CellIdentifier;
CustomCell *cell = [tableview dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.row];
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
It also cache downloaded images and gives you great performance.
Hope it will help you!

I hope this link help for you. by using asyn classes image loading async...
https://github.com/nicklockwood/AsyncImageView

Related

Images in UITableView changes as I scroll

I'm trying to create a feed just like the one in facebook. The problem is, the image on the succeeding rows will load the images from the initial rows and then correctly load their corresponding load. When you go to the top rows, the images previously loaded are gone. I've tried lazy loading but the problem persists. You could view the video to understand the problem better. (https://www.youtube.com/watch?v=NbgYM-1xYN4)
The images are asynchronously loaded and are fetched from our server.
Here are some Code:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [latestPosts count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary * dataDict = [latestPosts objectAtIndex:indexPath.row];
CardCell *cell = [self.feedTable dequeueReusableCellWithIdentifier:#"CardCell"];
if (cell == nil) {
cell = [[CardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CardCell"];
}
[cell layoutSubviews];
NSURL *imageURL = [[NSURL alloc] initWithString:[dataDict objectForKey:#"post_holder_image"]];
NSURL *postImageURL = [[NSURL alloc] initWithString:[dataDict objectForKey:#"post_image"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
NSData *postImageData = [NSData dataWithContentsOfURL:postImageURL];
dispatch_async(dispatch_get_main_queue(), ^{
cell.brandImage.image = [UIImage imageWithData:imageData];
cell.postImage.image = [UIImage imageWithData:postImageData];
});
});
cell.brandName.text = [dataDict objectForKey:#"post_holder"];
cell.postDateTime.text = [dataDict objectForKey:#"post_datetime"];
cell.postMessage.text = [dataDict objectForKey:#"post_content"];
return cell;
}
Use below method of UITableViewCell in your custom cell and set the image property to nil.
hope it will work for you.
-(void)prepareForReuse{
[super prepareForReuse];
// Then Reset here back to default values that you want.
}
There are a few problems with the above.
As mentioned above you need to use a image as a placeholder (i.e blank white or an image of your choice) in the cell init code AND cell reuse.
You really need to cache your images, only download an image once and then store it in a dictionary. i.e.:
UIImage *cachedImage = self.images[user[#"username"]];
if (cachedImage) {
//use cached image
[button setBackgroundImage:cachedImage forState:UIControlStateNormal];
}
else {
//download image and then add it to the dictionary }
where self.images is an NSMutableDictionary. You could also look into NSCache. If you don't cache the images you will find the table is very laggy when scrolling for a large number of rows because of the image conversion from data.
However this will not completely fix the problem if you start loading a table and scroll up and down very fast the images will appear to be in the wrong places and move around until they are all loaded. Cell reuse will confuse where to put the image. Make sure you put [tableView reloadItemsAtIndexPaths:#[indexPath]]; in your download block i.e.:
NSURL *imageURL = [[NSURL alloc] initWithString:[dataDict objectForKey:#"post_holder_image"]];
NSURL *postImageURL = [[NSURL alloc] initWithString:[dataDict objectForKey:#"post_image"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
NSData *postImageData = [NSData dataWithContentsOfURL:postImageURL];
dispatch_async(dispatch_get_main_queue(), ^{
cell.brandImage.image = [UIImage imageWithData:imageData];
cell.postImage.image = [UIImage imageWithData:postImageData];
[tableView reloadItemsAtIndexPaths:#[indexPath]];
});
});
You need to set imageview.image nil or you should set your placeholder image while reusing cells. Here is same question Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling
Other than that if you are not using parse.com api ect. you can check https://github.com/rs/SDWebImage or https://github.com/AFNetworking/AFNetworking
There are tons of answer about this topic.
[cell layoutSubviews];
cell.brandImage.image = nil;
cell.postImage.image = nil;

Images are loading in Table View in simulator but not in Device

I'm parsing images from JSON data and displaying in Table View. My code to display images in table view is -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellidentifier=#"MyCell";
OnlineCell *cell = [tableView dequeueReusableCellWithIdentifier:cellidentifier];
if (cell == nil)
{
cell = [[OnlineCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
}
cell.userLabel.text = [self.allusername objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
imageURL = [self.alluserphoto objectAtIndex:indexPath.row];
img = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:imageURL]]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.userIMage.image = img;
[indicator stopAnimating];
});
});
return cell;
}
Simulator Output -
Device OutPut -
Please tell me the how could i get images in device.
As i can see two screenshot one is Simulator and one is Device that two image common Load at both end simulator and device. So i think issue in to you image that comes from web side. Because you code is correct if there is issue in code then that not load two image as well in device.
Please test with change the image with First loaded image from your back End side and check once that have to load. put first loaded image for all response instead of goggles image that not load in device. i am dame sure issue is in image not in code.
Debugging Suggestion 1: On the device, make sure that the image is accessible. You can try opening the image in Safari. If safari is opening the image, it means that image is accessible.
Debugging Suggestion 2: If the image is accessible, try loading the image synchronously and debug if you're getting the required data (on the device):
NSURL *url = [NSURL URLWithString:self.photoImagePath]; // Is URL valid?
NSData *imageData = [NSData dataWithContentsOfURL:url]; // Is data nil?
UIImage *image = [UIImage imageWithData:imageData]; // Is image nil?
Then, try loading the image asynchronously, like this:
__weak UIImageView *weakImgageView = cell.userIMage;
imageURL = [self.alluserphoto objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:imageURL]]];
dispatch_async(dispatch_get_main_queue(), ^{
weakImgageView.image = image;
[indicator stopAnimating];
});
});
Hope this helps.
The default image is not coming properly. This is what I suspect. Please check if you are giving the name properly or not. This could be the issue.
Set default image until you fetch image asynchronously from JSON.
UIImage *img = [UIImage imageNamed:#"default.png"];
When you fetch image in asynchronously method means it will fetch depend on image size & internet speed. So you just keep a default image until it fetch completely from server
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
OnlineCell *cell = (OnlineCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSArray *nib = nil;
if (cell == nil)
{
nib = [[NSBundle mainBundle] loadNibNamed:#"YourCustomCellNibName" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Your code
cell.userLabel.text = [self.allusername objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.alluserphoto objectAtIndex:indexPath.row]]];
NSURL* url = [NSURL URLWithString:[self.alluserphoto objectAtIndex:indexPath.row]];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
UIImage* image = [[UIImage alloc] initWithData:data];
// do whatever you want with image
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
OnlineCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
cell.userIMage.image = image;
});
}
}
}];
});
}

UITableView content of a row loads after scrolling the cell out of the view

the problem is, that the content of the cell loads after i scrolled the cell out of the view:
i dont know how to describe it correctly, so i recorded a short 10 seconds video: http://youtu.be/GJouA3IXY0M
and i dont know how to fix it...
this is my cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PlaylistenTableViewCell *cell = [self.playlistenTableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//titleLabel = label which shows the big headline
cell.titleLabel.text = [self.JsonTitle objectAtIndex:indexPath.row];
cell.titleLabel.layer.cornerRadius = 5;
cell.titleLabel.layer.masksToBounds = YES;
NSString *urlString = [self.JsonContent objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *tmpImage = [[UIImage alloc] initWithData:data];
//playlistThumbnailImageView = the imageVIew which shows the Image
cell.playlistThumbnailImageView.image = tmpImage;
[cell.playlistThumbnailImageView setOpaque:NO];
[cell.playlistThumbnailImageView setBackgroundColor:color];
cell.playlistThumbnailImageView.layer.cornerRadius = 5;
cell.playlistThumbnailImageView.layer.masksToBounds = YES;
cell.playlistThumbnailImageView.layer.borderWidth = 4.0;
return cell;
}
You are most probably reloading the table with background thread. Make UI updates with main thread. So, after your data is fetched in the background, use this code.
dispatch_async(dispatch_get_main_queue(), ^{
[self.myTableView reloadData];
});
Edit:
Since you have already reloading the table using main thread, I suspect that image is taking time to download and in turn it is blocking the thread. I also observed some glitch happening in your video. Try to download the image using asynchronous methods and check. initWithContentsOfURL will block your thread and is not a good option in this place.
Try this code. Let me know in case of any query...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
-------
-------
NSString *imageUrl = #"-- URL string --";
NSURL *urlImage = [NSURL URLWithString:imageUrl];
[imgVw setImage:[UIImage imageNamed:#"placeholder.png"]];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
NSError* error = nil;
NSData *imageData = [NSData dataWithContentsOfURL:urlImage options:nil error:&error];
if(error){
dispatch_sync(dispatch_get_main_queue(), ^{
[imgVw setImage:[UIImage imageNamed:#"placeholder.png"]];
[self.loadingIndicator stopAnimating];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
});
}else{
UIImage *image = [UIImage imageWithData:imageData];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgVw setImage:image];
[self.loadingIndicator stopAnimating];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
});
}
});
}

How make parse image from JSON

I have some project where i must paste image from JSON. I try to find any tutorial about this, try to see any video from this theme but nothing. So my problem have:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSDictionary *allDataDictionary = [NSJSONSerialization JSONObjectWithData:webdata options:0 error:nil];
NSDictionary *playlist =[allDataDictionary objectForKey:#"playlist_data"];
for (NSDictionary *diction in playlist) {
NSString *name = [diction objectForKey:#"text1"];
NSString *namesong = [diction objectForKey:#"text2"];
NSString *images = [diction objectForKey:#"image"];
NSLog(#"%#", images);
[array addObject:text1];
[array2 addObject:text2];
}
[[self tableTrack]reloadData];
}
I added text 1 to cell also text 2 its work perfect but how to add image to to array 3(image in cell in my tableView)?
I tried also to added for image but its not work for me:
NSURL *imageURL = [NSURL URLWithString:[appsdict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
cell.imageView.image = imageLoad;
Please help with my problem or maybe give some tutorial with parse image from JSON, also youtube haven't perfect tutorial from JSON parse. Thanks!
Don't keep multiple data source like array1, array2, array3 etc. It is not good coding and will confuse while debugging/fixing issues. Instead maintain single array with information for displaying in individual cell of the table view.
for (NSDictionary *dict in playlist) {
// array is single data source
[array addObject:diction];
}
Then while assigning data to the table view cell use,
- (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];
}
cell.text1 = [[array objectAtIndex:indexPath.row] objectForKey:#"text1"];
cell.text2 = [[array objectAtIndex:indexPath.row] objectForKey:#"text2"];
//For displaying image by lazy loading technique use SDWebImage.
[cell.imageView setImageWithURL:[NSURL URLWithString:[[array objectAtIndex:indexPath.row] objectForKey:#"image"]] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
// If no place holder image, use this way
// [cell.imageView setImageWithURL:[NSURL URLWithString:[[array objectAtIndex:indexPath.row] objectForKey:#"image"]] placeholderImage:nil];
}
Like I mentioned in comments for lazy loading images from the URLs in JSON response, use SDWebImage. Usage is simple like I have shown above. Alternately you can implement lazy loading yourself by studying this sample code from Apple: LazyTableImages
Hope that helps!
According to https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html and http://www.json.org/ you can't keep binary data in JSON (unless it is in base64).
It seems there's a problem with your webdata which is not a valid JSON. Did you check what is set under #"image" key in that dictionary while debugging? You can use [NSJSONSerialization isValidJSONObject:webdata] as well to see if your data is ok.
//prepare your method here..
-(void)hitimageurl{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:replacephotostring]];
//set your image on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
if (_photosArray==nil) {
_showImageView.image = [UIImage imageNamed:#"noimg.png"];
} else {
[_showImageView setImage:[UIImage imageWithData:data]];
}
});
});

How to reduce time when UITableView added Multiple NSURL UIImage on cell its UIBlocked

I am trying to load an image form the internet into a cell.
When I'm using a single row then it's not taking much time, but when I have more then 5 rows then it is blocking UI. How can I solve this?
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
In this method: I am using that Code:
NSURL *url = [NSURL URLWithString:upcImageLink];
NSData *data = [NSData dataWithContentsOfURL: url];
UIImage *imageObj = [[UIImage alloc] initWithData:data];
[iconImgVw setImage:imageObj];
If I understand correctly, you are currently, making sync calls to download the tableview cell image. Sync call takes time and your screen/UITableView becomes unresponsive to touch events. The technique to avoid this is called Lazy loading.
Use SDWebImage for lazy loading of tableview images. Usage is simple,
#import <SDWebImage/UIImageView+WebCache.h>
...
- (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;
}
Alternatively, you can also implement lazy loading of image on your own refering to the Apple sample code.
Hope that helps!
please try the following code by replacing url:
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^(void)
{
NSData * data = [[[NSData alloc] initWithContentsOfURL:URL] autorelease];
UIImage * image = [[[UIImage alloc] initWithData:data] autorelease];
dispatch_async( dispatch_get_main_queue(), ^(void){
if( image != nil )
{
[iconImgVw setImage:image];
} else {
//error
}
});
});

Resources