table view with images which are loaded from server in iphone - objective-c

Hi I am developing IOS application. My application contains table view with image as part of my table. I am loading my image from server. It is working fine. But problem with my table view is once I scroll my table view it is start flickering my images. That mean it showing wrong image for some time after some time it shows right images. this behaviour continues when I scroll. Is there any need to explicitly call any object to nil or release some cell object or holding some cell objects. My cell for table view looks like :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"MediaContentCell";
MediaContentCell *cell = (MediaContentCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = (MediaContentCell *)[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
VideoDataModel *videoData = [_mediaContentArray objectAtIndex:indexPath.row];
cell.mediaTitle.text = videoData.title;
cell.mediaSubtitle.text = videoData.shortdescription;
NSMutableArray *posters = videoData.poster;
dispatch_queue_t myQueue = dispatch_queue_create("ImageQue",NULL);
dispatch_async(myQueue, ^
{
UIImage *image;
if(!posters)
{
image = [UIImage imageNamed:#"dummyprog_large1.png"];
}
else
{
for(int index = 0 ; index < posters.count; index++)
{
PosterDataModel *posterData = [posters objectAtIndex:index];
if([posterData.postertype isEqualToString:POSTER_TYPE_LANDSCAPE])
{
image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:posterData.posterurl]]];
break;
}
}
}
dispatch_async(dispatch_get_main_queue(), ^
{
cell.mediaPicture.image = image;
});
});
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
Is there any one can help me for this? Need some help Thank you.

use - SDWebImage
[cell.imageView sd_cancelCurrentImageLoad];
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
I am using this framework and Its working fine for me.

cell.imageView.image = [UIImage imageWithData:yourDefaultImgUrl];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:yourServerImageUrl];
if (imageData){
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
updateCell.imageView.image = [UIImage imageWithData:imageData];
});
}
});
It helps you :)

It's the reuse mechanism that causes your problem
Say, if your table view has 1000 cells to display, and 5 cells are visible on the screen at a time, iOS will only create 6 UITableViewCell objects in the memory.
In your code, the image will be loaded asynchronously from the network, and then set to the [mediaPicture] belonging to the cell at an index path, but when you scroll the table view, because of the reuse mechanism, the [cell.mediaPicture] might not be correctly related to the index path anymore, and the downloaded image will be wrongly set.
You can take a look at this question about the reuse mechanism:
UITableViewCell - Understanding "reuseable"
And Amol's answer is exactly the solution for your case.

Related

Objective C UI Collection issue

Description :-
Hello , When we are running this code when is cell relocate image is changing on scrolling.
Please let me know how to fix.
- (UICollectionViewCell )collectionView:(UICollectionView )collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cellidentifier";
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSString * string = [_temp objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// retrive image on global queue
UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:string]]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = img;
});
});
return cell;
}
The problem here is that if the cells get reused the previous image might still be loading or be set. You can override the cell's prepareForReuse to nil the image. For asynchronous loading of images you should check if the cell's model object is stil the same as when you started the loading, if not, then don't set the image as it arrived too late.

Asynchronous loading image inside uitableviewcell

Hello I am trying to use EGOImageView inside a CustomTableViewCell who i made to customize the cell. This is the code where I used the EGOImageView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString* simpleTableIdentifier = #"Albums";
CustomTableCell* cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (!cell)
{
cell = [[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
NSLog(#"Show once or more times");
}
NSDictionary* dictionary = (NSDictionary*)[self.albumCollection objectAtIndex:indexPath.row];
cell.label.text = [dictionary valueForKey:#"name"];
EGOImageView* imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageWithContentsOfFile:#""]];
[imageView setImageURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small&access_token=%#", (NSString*)[dictionary valueForKey:#"id"], [[FBSession activeSession] accessToken]]]];
[imageView setFrame:CGRectMake(0.0f,0.0f,78.0f,78.0f )];
[cell.iView addSubview:imageView];
[imageView release];
The image on each cell loading the same image. Would it be because it reused the cell while loading the image.
I found a problem I can't think of why the problem happened. I used the graph api to grab the image https://graph.facebook.com/%#/picture?type=small&access_token=%# where the first parameter was the album id.
To make myself easy to see the problem I only used one album on the cell, no matter what album i used the same photo turned up. But when I copy the link to the browser, the actual photo url shown on the address bar with the image shown and it shown the correct photos.
Does anyone know what was wrong.
Here is example. It loads user pics from some server in background and updates cell image. Note that imageView.image is set to nil at the beginning. This is dome for the case of cell reuse so that you will have no image rather than wrong image for the time while its downloading.
One more thing to add is that, would be also good to have a cache so that it does not download images all the time. Another nice thing is to not download images in edge networks.
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TransactionCell";
NSMutableArray *data = searching ? searchResult : dataSource;
NSDictionary *object = [data objectAtIndex:[indexPath row]];
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithIdentifier:CellIdentifier] autorelease];
}
cell.imageView.image = nil;
cell.textLabel.text = #"Your cell text";
NSString *contact = #"foo#gmail.com";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imgData = [appDelegate addUserPic:contact];
if (imgData == nil && netStatus == ReachableViaWiFi) {
NSString *url = [NSString stringWithFormat:#"http://somehost.com/userpic/%#", contact];
imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
}
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *updateCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
if (imgData) {
[appDelegate setUserPic:contact imgData:imgData];
updateCell.imageView.image = [UIImage imageWithData:imgData];
} else {
updateCell.imageView.image = nil;
}
/* This forces the cell to show image as now
it has normal bounds */
[updateCell setNeedsLayout];
}
});
});
return cell;
}

TableView with little less performance

I'll try to implement a TableView with 2 different kinds of cells, the first one is a higher Cell with a fullwidth image inside, all other cells got a little thumb image on the left side.
My problem it the performance of the TableView. If I scroll a list of maybe 20 items it jerks a bit. I've red something the performance and I'll hope the code is not so bad:
"Caching of images is not necessary to raise the performance" Is this right?
Does I reuse the cells in the right way.
Is that the normal way to use 2 different kinds of Cells?
Here is the important Part:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == 0) {
return 160;
}
return 60;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellName = [[NSString alloc] init];
// [...]
if(indexPath.section == 0 && indexPath.row == 0){
cellName = #"FirstMainCell";
CellTemplateFirstNews *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
if(cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:#"CellTemplateFirstNewsView" owner:nil options:nil] objectAtIndex:0];
}
NSURL *urlRowImage = [NSURL URLWithString:[[NSString alloc]initWithFormat:#"http://webserver.de/inhalte/news/title/%#", detailDataNews.title_picture]];
NSData *dataRowImage = [NSData dataWithContentsOfURL:urlRowImage];
UIImageView *firstNewsImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 160)];
firstNewsImageView.backgroundColor = [UIColor clearColor];
firstNewsImageView.opaque = NO;
firstNewsImageView.image = [UIImage imageWithData:dataRowImage];
cell.backgroundView = firstNewsImageView;
// [...]
return cell;
}else{
cellName = #"MainCell";
CellTemplateNews *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
if(cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:#"CellTemplateNewsView" owner:nil options:nil] objectAtIndex:0];
}
NSURL *urlRowImage = [NSURL URLWithString:[[NSString alloc]initWithFormat:#"http://webserver.de/inhalte/news/cover/%#", detailDataNews.cover_picture]];
NSData *dataRowImage = [NSData dataWithContentsOfURL:urlRowImage];
UIImage *rowImage = [UIImage imageWithData:dataRowImage];
cell.thumbImage.image = rowImage;
// [...]
return cell;
}
}r
I think the main problem is the synchronous network request that's happening for every cell, whether it's reused or not. The culprit is dataWithContentsOfURL:.
Check out this apple sample code, or google the phrase "ios lazy table images". In this SO answer, I provide a fairly simple to implement method that takes care of the loading an image asynch, then finding and updating the correct table view row upon completion.
One posibility is that you've forgotten to enter the CellIdentifier in your Xib. In fact the reuseIdentifier method needs your cell to have the same identifier, but you never set it when you create your cell. Those cells in your Xib have to have their identifier entered.
Do not alloc-init any object in cellForRowAtIndexPath You are probably leaking massive memory. Remember, this method called every time a cell comes into view, even cells that have been previously shown.
Also, your assignments to string variables are redundant. It is sufficient to do it like this.
BOOL firstCell = (indexPath.row==0 && indexPath.section==0);
NSString *cellIdentifier = firstCell ? #"FirstMainCell" : #"MainCell";
UITableViewCell *genericCell = [tableView
dequeueReusableCellWithIdentifier:cellIdentifier];
if (firstCell) {
cell = (CellTemplateFirstNews*) genericCell;
// configure...
}
else {
cell = (CellTemplateNews*) genericCell;
// configure...
}
return cell;
The other possible reason for your lag is the synchronous call to the web service. You are doing this each time the cell becomes visible. You need to load the images lazily and update the UI when necessary.

why UITableView is showing data when I touch it?

I have a UITableView loading some tweets, all works nicely but when I populate the table it doesn't enter to the CellForRow method til I touch the table view.... does somebody know how to solve this?
tab = [[UITableView alloc] initWithFrame:CGRectMake(10, 300, 400, 200) style:UITableViewStylePlain];
tab.dataSource = self;
tab.delegate = self;
then...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"entra a cellula");
Tweet *t = [tweets objectAtIndex:indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil) {
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = t.screenName;
cell.detailTextLabel.text = t.text;
cell.imageView.image = t.profileImage;
return cell;
}
Best guess based on what we know is that you aren't adding the table as a subview on the main thread. This means that redrawing doesn't occur until you scroll. Try wrapping the addsubview call in a GCD call like so:
dispatch_async(dispatch_get_main_queue(), ^() {
[self addSubview:tab];
});
When you finished loading your tweets call this method:
[tab reloadData];
to reload your tableView data source.

dequeueReusableCellWithIdentifier causing problem on UIImageView

I am loading a tableView with 500 rows. The problem is that in each row there is a different picture. Or when I use dequeueReusableCellWithIdentifier, those picture are just loaded again and the real pictures I am looking for are not shown (I just have about 8 different pictures : the first 8 loaded on my screen). If I don't use the dequeureReusableCellIdentifier, all the picture are loaded. But will it slow the displaying ?
Here is the code (I am currently working on getting the picture cached) :
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier: CustomCellIdentifier];
NSLog(#"Launching CellForRowAtIndexPath");
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self options:nil];
if ([nib count] > 0) {
cell = self.profilCell;
} else {
NSLog(#"failed to load CustomCell nib file!");
}
}
NSUInteger row = [indexPath row];
NSDictionary *rowData = [listProfils objectAtIndex:row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:nameValueTag];
nameLabel.text = [rowData objectForKey:#"name"];
NSString *finalId = [NSString stringWithFormat:#"http://graph.facebook.com/%#/picture", [rowData objectForKey:#"id"]];
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:finalId]]];
[profilPic setImage:image];
return cell;
}
THank you ! :)
It looks like you have an ivar profilPic that is probably an outlet that gets linked when you load a new cell nib. If that's the case, it's always going to point to the last cell that you loaded and won't change the image in the cell you've just dequeued. Instead of using an outlet, you might want to identify that custom view some other way, like a tag. So, if you set the profile pic UIImageView's tag to 100, for example, in Interface Builder, you could do something like this:
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:finalId]]];
UIImageView* cellImageView = (UIImageView*)[cell viewWithTag:100];
[cellImageView setImage:image];
Also, I just want to point out that -dataWithContentsOfURL: will load the URL synchronously on the main thread. If you're testing in the simulator on a fast connection, this will work pretty well. If, however, you are on 3G in SoHo on Friday afternoon... your app will probably start being killed by the watchdog.
I just met this problem, my solution is hold a private NSMutableDictionary to store the new images which asynchronously loaded from web before, use my identifier as key, UIImageView as Object (because I need to load the icon image first), when web image is ready, change it, When tableView dequeue return's null, I can read the original UIImage from my own cache.
Something like this.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
UIImageView *imageView = [thumbnailCache objectForKey:identifier];
if (!imageView) {
cell.imageView.image = [UIImage imageNamed:#"icon.png"];
[thumbnailCache setObject:cell.imageView forKey:identifier];
} else {
cell.imageView.image = imageView.image;
}
}
When I load the actual image from web, refresh the thumbnail cache.
asynchronously_load_image_from_web(^(UIImage *image) {
cell.imageView.image = image;
[thumbnailCache setObject:cell.imageView forKey:identifier];
});