UIImageView+AFNetworking image not loading correctly in UITableViewCell - ios

I'm using the setImageWithURL to lazily load images into my tableview cells but there is a problem. It loads the images fine but there seems to be a refresh problem on the device where the images only show when you scroll them out of the view and back in again.
It works perfectly in the simulator. The device is running iOS 5.1.1 on a iPhone 4S
This table is displayed in a UINavigation controller and as the images are cached I expect the images to appear pretty quickly when I revisit the screen. They do, but the show up at half their original size.
The images are 60x30 in size.
Is this an issue with loading images into a retina screen that they are half the size and what would cause this refresh problem?
Code snippet below...
- (UITableViewCell *)tableView:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
...
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"transparent_shade"]];
...
return cell;
}
Thanks in advance for any insights

I would suggest loading/preparing the images before the cellForRowAtIndexPath method. The reason you are seeing the images after scrolling is because the images were previously downloading at the time this method was called, hence the files were not yet available.
Make a NSMutableArray inside your view's viewWillAppear method (override it if non-existent) for instance, and put all the images inside the array. Then, in the cellForRowAtIndexPath simply get the images from the array and assign them as needed; they should be displayed on demand, without delays.
Let us know how this works for you.

Related

Show very big size images to the tableview cell

I want to show some images that user saved in his Document directory in a uitableview. I use below simple code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"uploadHistoryCellfa";
BRUploadHistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[BRUploadHistoryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
//Some Other assignemt to UILables Here...
[cell.imagePreview setImage:[UIImage imageWithContentsOfFile:myImagePathInDocument]];
//Some other configurations here ....
return cell;
}
my question is that:
The images size is about 11-25 MB. and I will load one image at each cell. Does this code that I use potentially can cause memory leak or some thing like this?
11-25 MB is large file actually if you have UITableView you will have more then 5 images so it will become 11-25 (*5) so you should resize images while adding them on tableView, but if you will resize it in proccess of showing it will slow up your app. So you first step is to save resized image which will size of max 1 mb.
Second Step is to generate thumbnail and after that add thumbnail image on cell.
First Link: Resize Image
Second Link: Image Resizing Techniques
imageIO for my practice is fastest way to resize image because it uses CPU and user will not see slow scrolling problem.
After all steps done do not forget to use foreground thread and main thread for representing images on cell.
If only 1-2 cells are shown in screen at a time then it won't cause you much trouble.
As TableView will update the contents of already created cells with new Images. (Deque Reusable Concept)
But the better Approach will be to resize the Images first and then use.

Loading images asynchronously, weird problems

There is my question:
i have custom class that parse through an XML and get string i need to use as URL for my strings, now i modified my code as follow:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *labelText = (UILabel *)[cell viewWithTag:1000];
labelText.text = [[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:#"name"];
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
});
return cell;
}
This is pretty straightforward, but, i got unpredictable errors! During scrolling table, images start to chaotically change, sometimes it show 3 or more images and final image is correct one, sometimes final (correct) image does not appear at all. Also, when table is first shown, its actually blank, so i need to scroll it bottom, and then up again to see my images!
In attempt to fix that, i add following code, to determine is my image link correct for that indexPath:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSLog(#"%#", imageURL);
}
And when i tap to any cell, it does show me proper link in console log, but image on cell is one of the image shown before (invalid), and it is not the image for that link. How to fix that weird errors?
Any advice would be appreciated, thank you.
When you dequeue a cell object, most of the time you'll get a reused cell i.e. a cell that has been configured by tableView:cellForRowAtIndexPath: once or more times before.
To visualise what's happening in your case, consider one likely sequence of events for a single cell as you perform a long, quick scroll:
The cell is created and an image load is spun off in the background
The cell is scrolled off screen, so added to the table view's cache, ready for dequeuing. The image loading is not canceled at this point
The cell is dequeued and an image load is spun off in the background
Steps 2 and 3 are repeated a few times
The cell is visible, but the several image loading tasks are now completing and each is updating the cell's imageView with the loaded image. This will indeed look like the images are chaotically changing as each loading operation finishes.
(What's more, with a concurrent queue, there's no guarantee that the image loads will complete in the order that they're started - you may not end up with the correct final image!)
So what do we do about it? Now that we understand the problem, there are lots of different solutions. A very simple solution (that I don't really recommend) is to check that the cell's label text matches the value for that indexPath, when you come to set the image:
if ([labelText.text isEqualToString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:#"name"]]) {
cell.imageView.image = [UIImage imageWithData:image];
}
Obviously, this assumes that all the place details have unique names.
A better solution might be to create an object that handles the image download, and is something that you can register/unregister cells against to handle download completion. This object could enforce the condition that a cell cannot be waiting for more than one image load. As #Leena pointed out, caching is a good idea and this object could be responsible for that too.
As for the blank images, calling [cell setNeedsLayout] after setting the image should sort that out.
Default property "imageView" will not be added to the cell until its (imageView's) property "image" is nil (you can check cell.imageView.superview will be nil too).
That's why your tableView is blank when it is loaded and all images for cells are also loaded.
So when you scroll it down (or up) cells will be reloaded, their "imageView" will have image data. That is the reason why they are on cell and you can see them.
The other problem is that your images are flashing all the time. It happens because your cells are dequeued.
So, when the image for the first cell is downloaded and setted it will not be shown until you relayout cell's subviews (for example by calling [cell setNeedsLayout];-).
And when you scroll table down, your first cell (with an image now) will be dequeued from "tableView" and will become last cell, and then your first cell will be shown with image that actually belongs to the first row.
At the same time you start downloading image for this (last) row and after it was downloaded you will set it. And this is the monent when flashing happens.

UITableView Cells not doing dealloc

I'm building a UItableView with custom cells that displays images. The problem is that as I scroll down, the previous cells does not seem to do a dealloc and the device memory is filling up.
This is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
CustomTableCellView *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[CustomTableCellView alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
Article *tempArticle = [_objects objectAtIndex:indexPath.row];
cell.titel.text = tempArticle.title;
cell.subTitle.text = tempArticle.subTitle;
cell.category.text = tempArticle.category;
if(tempArticle.imageUrl){
[cell.image setImageWithURL:[NSURL URLWithString:tempArticle.imageUrl]];
} else {
cell.image.image = [UIImage imageNamed:#"video"];
}
return cell;
}
I'm 99% sure that the memory increase is due to the image loading & caching. The AFnetworking it's using a NSCache instance to cache the downloaded images, this cache is performed in memory and it will be drained when the system decide it has low memory, this is not an issue.
Also the imageNamed method caches the images that are loaded in the same way as AFNetworking.
In order to check if you have a leak or something is not working properly with your data, check if the memory increases after you scroll back up, if the memory stays the same, than you don't have a problem nor a leak (also you can check leaks with instruments), the cached images are the ones that cause the increase of memory.
Also remove the view controller (pop, remove) from the stack, this will cause some deallocations on the text, labels or other data (not images), if the memory decrease (it won't be a substantial decrease) then you are on the right way.
As stated above, you are setting the reuse identifier correctly.
Maybe it's filling the memory because you're using AFNetworking library and downloading the images for your cells? Define "filling up the memory" more exactly please.

UIImageView not loading in UITableViewCell until scrolled

I am having a problem with a UIImageView I am setting inside tableview:cellForRowAtIndexPath: for some reason when the UITableView loads the bottom cell half in view dose not have the a Image loaded into the UIImageView like the rest of the UITableViewCells, however once I scroll then it works itself out.. But once doing so the top UItableViewCell then drops its image! untill i scroll that back into full view.
What I have done is created the UITableViewCell in Interface Builder and i have a blank UIImageView, I then set the UIImage I am going to place inside the UIImageView in ViewDidLoad then inside tableview:CellForRowAtIndexPath: I set it then return the cell.
heres the code
//.m
- (void)viewDidLoad
{
//..
storageCalcIconImage = [UIImage imageNamed:#"CALC.png"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
SeriesSearchResultItem *myObj = [[SeriesSearchResultItem alloc] init];
if (indexPath.section == 0) {
//call obj array with all of the values from my sorting algorithum
myObj = (SeriesSearchResultItem*)[dataArrayOfObjects objectAtIndex:indexPath.row];
//.. assinging storageCalcIconImage inside if statment
firstSpaceImage.image = storageCalcIconImage;
//..
}
return cell;
}
there is lots happening in tableView:CellForRowAtIndexPath but I decided to drop the stuff that wasnt related to the image problem.. hopefully you guys can see an error I am making that I havent yet... any help would be greatly appreciated.
This is an expected result from the use of reusable cells inside a tableView. If you want to avoid this behavior you should create a cell for each rows of your tableView instead of using dequeueReusableCellWithIdentifier.
Expect huge performance impact if you have a lot of rows to display though.

scrolling on tableView is so slow

i have a tableview which has image and a text behind, i create the cells like below code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
NSDictionary * companyProductRow = [DB getCompanyProductRow:[self.companyProductIDs objectAtIndex:indexPath.row]];
int companyProductID = [[companyProductRow objectForKey:#"ID"] intValue];
cell.tag = companyProductID;
cell.textLabel.text = [companyProductRow objectForKey:#"ImagePath"];
NSString* fullPath = [FileManager fullPath:companyProductID fairDirectory:[[self.currentFair objectForKey:#"ID"]intValue]];
[[cell imageView] setImage:[UIImage imageWithContentsOfFile:fullPath]];
return cell;
}
i read some tips about tableview performance on apple developer site but all of they said is :
Reuse cells. Object allocation has a performance cost, especially if the allocation has to happen repeatedly over a short period—say, when the user scrolls a table view. If you reuse cells instead of allocating new ones, you greatly enhance table-view performance.
Avoid relayout of content. When reusing cells with custom subviews, refrain from laying out those subviews each time the table view requests a cell. Lay out the subviews once, when the cell is created.
Use opaque subviews. When customizing table view cells, make the subviews of the cell opaque, not transparent.
the sample in apple site is the same, but i want to know is there any way to have better performance when scrolling on uitableview? (when we should read image from disk).
so thanks
Yes it is.
The first thing you could enhance is the image loading.
Try to avoid [UIImage imageWithContentsOfFile:fullPath] because it always loads the entirely image into the memory. This in fact is slow. Use [UIImage imageNamed:"YOUR_IMGAES_NAME"] instead because it caches the images after the first use or preload/store them directly in your fileManager.
The next step would be to set all views in the cell to nil (like the imageView) and draw all the contents by hand. The reason for this is that UIViews are pretty slow. If you have a lot of labels, images, etc. to display it happens to be much faster to draw everything by hand.
Your tableView:cellForRowAtIndexPath: looks like it's doing 2 disk reads for each cell, one from your [DB getCompanyProductRow:...] which I assume is a database fetch of some sort, and the second from the [UIImage imageWithContentsOfFile:...].
Find a way to load these information to memory, before tableView:cellForRowAtIndexPath: gets called (preferably before [tableview reloadData]).
Tips for large datasets:
If the data you need to fetch is too big for the memory to be loaded into an array, try to implement some paging mechanism so you just need to display a subset of that data. I would even recommend you use Core Data's NSFetchedResultsController instead, but that's if your database structure is compatible for object models.

Resources