I am using asyncimageview in the uitableview for displaying the image in the uitableviewcell but when scrolling the tableview the image are not displayed directly. it first display old one and the present the new image from the url. its not caching or not displaying from cache.
if (Cell == nil) {
Cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
Cell.selectionStyle = UITableViewCellSelectionStyleNone;
//Create Cell Method
[self CreateCell:FollowCell atIndexPath:indexPath]; }
// Set Cell Method
[self SetCell:FollowCell atIndexPath:indexPath];
[Cell setClipsToBounds:YES];
return FollowCell;
// Image code
[asyncImgUser setImageURL:[NSURL URLWithString:strUserImage] andPlaceHolder:[UIImage imageNamed:#"Placeholder.png"]];
}
TableViewCells are reused by TableView for this reason initially old image is shown and after your image is loaded by the async task it displays new image. And if it happens always it means your caching is working.
Related
Hi my problem is that when I scroll TableView the image will appear in a wrong cell, after a few seconds the correct image appears.
here is my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
[cell setOpaque:NO];
[cell setBackgroundColor: [UIColor clearColor]];
PlaceData *data = [tableData objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
UILabel *sciNameLabel = (UILabel *)[cell viewWithTag:200];
UIImageView *thumbnailImageView = (UIImageView *)[cell viewWithTag:300];
nameLabel.text = data.name;
sciNameLabel.text = data.scientific_name;
// 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, ^{
NSURL *urlToPicture = [NSURL URLWithString:[NSString stringWithFormat:#"%#", data.thumbnail]];
NSData *imgData = [NSData dataWithContentsOfURL:urlToPicture options:0 error:nil];
// This will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
thumbnailImageView.image = tmpImage;
//dispatch_release(concurrentQueue);
});
});
return cell;
}
please help me
You can try adding following code to your cellForRowAtIndexPath -
1) Assign an index value to your custom cell. For instance,
cell.tag = indexPath.row
2) On main thread, before assigning the image, check if the image belongs the corresponding cell by matching it with the tag.
dispatch_async(dispatch_get_main_queue(), ^{
if(cell.tag == indexPath.row) {
UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
thumbnailImageView.image = tmpImage;
}});
});
You are reusing old cells, which is a good thing. However, you are not initializing the image in the cell's image view, which is not such a good thing. What you're describing happens because an old cell, with an image that was already loaded for that cell, is used for the new cell. You are then loading that cell's image in the background (which, again, is good) but it takes a few moments for that image to fully load. In the meantime, the image that was already loaded on the old cell, is displayed (and that's the reason you're seeing a wrong image in that cell, for a few moments).
The solution? add either
thumbnailImageView.image = nil
or
thumbnailImageView.image = someDefaultImageWhileYourRealOneLoads
right before dispatch_queue_t concurrentQueue ....
That way, you won't see the old (irrelevant) image while the real one loads.
I hope this helps. Good luck.
As becauase your ImageView is being loaded in an async dispatch call which is NOT on the main thread and is being called in some other thread so there is a delay in fetching the data from the URL and then converting it to an UIImage. THis process takes a bit of time as you know but you are scrolling the tableview in a faster rate. And as you know cellForRowAtIndexPath reuses any cell that is out of the window so the cell that is being reused might NOT fetched the imagedata that it WAS TO RETRIEVE previously when it was in the Window. Thus it loads the wrong data and then again when async is fired for that specific cell the cell loads that image but there comes the delay.
To overcome this feature as Chronch pointed it out u can leave the imageview as nil OR you can use AFNetworking's own UIImageView catagory which has a superb class to load imageview images quite elegantly
I'll leave u a link to it AFNetworking
I would do all my data binding at - tableView:willDisplayCell:forRowAtIndexPath: only because at cellForRowAtIndexPath your cell hasn't been drawn yet.
Another solution you can use is AFNetworking like someone else mentioned before me.
Swift 3.0
DispatchQueue.main.async(execute: {() -> Void in
if cell.tag == indexPath.row {
var tmpImage = UIImage(data: imgData)
thumbnailImageView.image = tmpImage
}
})
cell.thumbnailimages.image=nil
cell.thumbnailimages.setImageWith(imageurl!)
I think these two lines solve your problem.
i am able to fetch images asynchronously on to uitableview.i am fetching these images are from a url.on scrolling up uitableview these images disappear and they take time to load again and sometimes they dont load at all.i dont want to use any 3rd party libraries.i dont want to go with synchronous approach.please suggest any correct approach to improve performance.thanks for help in advance.my code is below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier=#"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"identifier"];
}
UIImageView *imgVw=[[UIImageView alloc]initWithFrame:CGRectMake(250, 10, 40, 30)];
[cell.contentView addSubview:imgVw];
Attributes *att = [listOfObjects objectAtIndex:indexPath.row];
strImgUrl=#"http:image url";
strImgName=att.classifiedImg;
if (strImgName == nil) {
UIImage *myImg=[UIImage imageNamed:#"user_circle.png"];
imgVw.image=myImg;
}
else{
strImg=[strImgUrl stringByAppendingString:strImgName];
}
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData *data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:strImg]];
if ( data == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *img=[UIImage imageWithData: data];
imgVw.image=img;
});
});
return cell;
}
UITableViews are designed to reuse cells. When you scroll up for example the first cell might get reused to show the 5th cell since the 1st one is now off screen. When you scroll back up cellForRowAtIndexPath is called again, and you are async downloading the same image again. If you want to have it load instantly you will need to cache the images after downloading them the first time so the next time that image is needed you can directly pull it from the cache.
Many third party libraries do this (AFNetworking), but if you don't want to use them, you will have to cache the images manually.
It seems like your reuse code contains bug, which causes everytime to create new cells. The following code uses reuseIdentifier as #"identifier"
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"identifier"];
}
Change it to:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
EDIT:
On reusing tableView cells, you dont need to create and add subviews to cell each time. Instead, just create the subview when creating cell and then if cell is reusing, just get the subview using tag and update your content from datasource.
Your code can be modified like below:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
UIImageView *imgVw=[[UIImageView alloc]initWithFrame:CGRectMake(250, 10, 40, 30)];
imgVw.tag = indexPath.row;
[cell.contentView addSubview:imgVw];
}
UIImageView imgVw = (UIImageView*)[cell.contentView viewWithTag:indexPath.row];
//Rest is same as you posted.
Hope this will fix the issue.
-Hi, I have a problem with a tableview reloading data and what I want to do is that you put the selected cell in blue and change an image that is, the problem is that if I make the [self.mytableview reloadData] in the didselectedRow blue background disappears and if I do the image of the cell does not change, I'm a bit lost with this piece of code I give thanks
if (indexPath.row == _selectedRow) {
UIImageView *favView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"favIconSelected.png"]];
CGRect frame = favView.frame;
frame.origin.x = 294;
frame.origin.y = 7;
favView.frame = frame;
[cell.contentView addSubview:favView];
[favView release];
}
cell.selectedBackgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:SELCETED_BGIMGCELL]]autorelease];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Product *p = [_productList objectAtIndex:indexPath.row];
_selectedRow=indexPath.row;
[_delegate productWasSelected:p];
[self.myTableView reloadData];
}
You don't need to call reloadData at all. All you need to do is to update the cell that was selected.
In the didSelectRowAtIndexPath method you can get the cell directly by calling cellForRowAtIndexPath on the tableView. This will return the actual cell that's on display and you can directly set the image it's displaying.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = ...;
Alternatively you could control the image display from your cell subclass if you provide it with both the normal and selected images when you configure each instance.
My UITableView is pulling data from an array. Items in this array have a property called IsSelected. I am trying to put a UIImageView in the cell contentView for each item that is selected.
The UITableView however when reusing the cell is causing my image to be reused on cells that it shouldn't. I cannot figure out for the life of me how I should be doing it differently. I have attached a screen shot showing the issue. If I keep scrolling up and down the image goes all over the place:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SchoolCellIdentifer = #"SchoolCellIdentifier";
SchoolInfoItem *item = [self.schoolsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SchoolCellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SchoolCellIdentifer];
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
cell.textLabel.text = item.Name;
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:selectedItemCheckMarkIcon];
}
else
{
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
return cell;
}
You need to make sure the UIImageView is getting removed from the cell content view. It looks like in your code when a cell gets dequeued that imageview is still in the cells view hierarchy.
Best solution is to have your cell hold onto a reference to the image view and remove it when necessary.
Take the following:
if ([item.selected isEqualToString:#"1"])
{
cell.contentView.backgroundColor = [BVColors WebBlue];
cell.myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(300, 13, 17, 17.5)];
[selectedItemCheckMarkIcon setImage:[UIImage imageNamed:#"check-mark.png"]];
[cell.contentView addSubview:cell.myImageView];
}
else
{
[cell.myImageView removeFromSuperview];
cell.myImageView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note in the else case the removal of the imageview.
You keep adding a the UIImageView as a subview on the cell's contentView. This isn't removed when the table view is reusing the cell. You'll need to remove the subview if it shouldn't appear.
You should make selectedItemCheckMarkIcon a property on your UITableViewCell subclass. And then have a method in your subclass where you set the image or visibility of the image accordingly.
You could also use the accessoryView property on the UITableView and set the imageView as that:
if ([item.selected isEqualToString:#"1"]) {
cell.contentView.backgroundColor = [BVColors WebBlue];
UIImageView *selectedItemCheckMarkIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"check-mark.png"]];
cell.accessoryView = selectedItemCheckMarkIcon;
} else {
cell.accessoryView = nil;
cell.contentView.backgroundColor = [BVColors WebDarkBlue];
}
Note that you don't need to set a frame in this case because the system will automatically set the frame correctly for the accessoryView.
I want to show an image on the right side in a UITableViewCell.
I'm using this code, to do it:
CGRect imageRect = {80,0,225,44};
UIImageView *uiv = [[UIImageView alloc] initWithFrame:imageRect];
[uiv setImage:[UIImage imageNamed:[NSString stringWithFormat:#"star%#.png", [[self reataurant] valueForKey:#"stars"]]]];
[uiv setClipsToBounds:YES];
[cell addSubview:uiv];
My problem is, that the image is shown in other cells, when i'm scrolling the tableview.
What am i making wrong?
What is probably happening is that you are re-using cells that already have an image when you do not wish to. Check
a) when you re-use a cell, if it has the star image then remove it.
b) before you return the cell add the star at that point (with whatever test you use to determine if the star should appear).
Those UITableViewCells are reused. Look in your cellForRowAtIndexPath for
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
If you only want one or a certain set of cells to have this particular layout, you can pass a different cellIdentifier for these cells.
You can easily add an image view as the accessory view to a table cell for the same effect.
Here is the sample code for your understanding:
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"fileName"]];
cell.accessoryView = imageView;
[imageView release];
And make sure you are reusing cell space of a table view in cellForRowAtIndexPath method by using a cell identifier so that overwritten problem can be eliminated ;)
NSString *CellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
Hope that should eliminate your problem of images getting displayed on one another,thanks :)