I am using lazy loading to show images on a table view.
But I need to create a tableview with multiple images in every cell.Which can be scrolled.
All images are loaded from server only
How can I create this without any lagging for table scrolling ?
Is there any tutorial available for this
Try this code. SDWebImage. It downloads image from server and save it to device cache.
Also if you don't want save it to cache then you might have a look at AFNetworking.
There is another option. Using GCD (Grand Central Dispatch).
Example Code :
// Get the filename to load.
NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];
[[cell textLabel] setText:imageFilename];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
dispatch_sync(dispatch_get_main_queue(), ^{
[[cell imageView] setImage:image];
[cell setNeedsLayout];
});
});
Use the same for showing multiple images. Using this will increase the performance of loading tableview definitely.
Refer this to know more about GCD
Try this https://github.com/nicklockwood/AsyncImageView .Easy to download images asyncronously from server.
Related
I need to resize a large locally stored image (contained in self.optionArray) and then show it in the collectionView. If I just show it, iOS trying to resize the images as I scroll quickly causing memory-related crashes.
In the code below, the collectionView will scroll smoothly, but sometimes if I scroll extremely fast, there will be an incorrect image that shows and then changes to the correct one as the scrolling decelerates. Why isn't setting the cell.cellImage.image to nil fixing this?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
cell.cellImage.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
cell.cellImage.image = nil;
UIImage *test = [self.optionArray objectAtIndex:indexPath.row];
UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.cellImage.image = localImage2
cell.cellTextLabel.text = #"";
[cell setNeedsLayout];
});
});
}
return cell;
}
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
EDIT:
I added another async to cache first and nil and initialized the cell.image. I'm having the same issue on the initial fast scroll down. However, on the scroll back up, it's flawless now.
I added this:
-(void)createDictionary
{
for (UIImage *test in self.optionArray) {
UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
[localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:[self.optionArray indexOfObject:test]]];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (!localImageDict) {
localImageDict = [[NSMutableDictionary alloc]initWithCapacity:self.optionArray.count];
}
else {
[localImageDict removeAllObjects];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[self createDictionary];
});
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
cell.cellImage.image = nil;
cell.cellImage.image = [[UIImage alloc]init];
if ([localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]]) {
cell.cellImage.image = [localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]];
cell.cellTextLabel.text = #"";
}
else {
cell.cellImage.image = nil;
cell.cellImage.image = [[UIImage alloc]init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *test = [self.optionArray objectAtIndex:indexPath.row];
UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
[localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:indexPath.row]];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.cellImage.image = shownImage;
cell.cellTextLabel.text = #"";
[cell setNeedsLayout];
});
});
}
}
return cell;
Taking a closer look at your code sample, I can see the source of your memory problem. The most significant issue that jumps out is that you appear to be holding all of your images in an array. That takes an extraordinary amount of memory (and I infer from your need to resize the images that they must be large).
To reduce your app's footprint, you should not maintain an array of UIImage objects. Instead, just maintain an array of URLs or paths to your images and then only create the UIImage objects on the fly as they're needed by the UI (a process that is called lazy-loading). And once the image leaves the screen, you can release it (the UICollectionView, like the UITableView does a lot of this cleanup work for you as long as you don't maintain strong references to the images).
An app should generally only be maintaining UIImage objects for the images currently visible. You might cache these resized images (using NSCache, for example) for performance reasons, but caches will then be purged automatically when you run low in memory.
The good thing is that you're obviously already well versed in asynchronous processing. Anyway, the implementation might look like so:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
NSString *filename = [self.filenameArray objectAtIndex:indexPath.row]; // I always use indexPath.item, but if row works, that's great
UIImage *image = [self.thumbnailCache objectForKey:filename]; // you can key this on whatever you want, but the filename works
cell.cellImage.image = image; // this will load cached image if found, or `nil` it if not found
if (image == nil) // we only need to retrieve image if not found in our cache
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *test = [UIImage imageWithContentsOfFile:filename]; // load the image here, now that we know we need it
if (!test)
{
NSLog(#"%s: unable to load image", __FUNCTION__);
return;
}
UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
if (!localImage2)
{
NSLog(#"%s: unable to convert image", __FUNCTION__);
return;
}
[self.thumbnailCache setObject:localImage2 forKey:filename]; // save the image to the cache
dispatch_async(dispatch_get_main_queue(), ^{ // async is fine; no need to keep this background operation alive, waiting for the main queue to respond
// see if the cell for this indexPath is still onscreen; probably is, but just in case
CustomTabBarCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (updateCell)
{
updateCell.cellImage.image = localImage2
updateCell.cellTextLabel.text = #"";
[updateCell setNeedsLayout];
}
});
});
}
return cell;
}
This assumes that you define a class property of thumbnailCache which is a strong reference to a NSCache that you'll initialize in viewDidLoad, or wherever. Caching is a way to get the best of both worlds, load images in memory for optimal performance, but it will be released when you experience memory pressure.
Clearly, I'm blithely assuming "oh, just replace your array of images with an array of image filenames", and I know you'll probably have to go into a bunch of different portions of your code to make that work, but this is undoubtedly the source of your memory consumption. Clearly, you always could have other memory issues (retain cycles and the like), but there's nothing like that here in the snippet you posted.
I had a similar problem but went about it a different way.
I also had the issue of "pop-in" as images that were loaded async were flashed as they come in until finally the correct one was shown.
One reason this is happening is that the current indexpath for the cell that was initially dequeued did't match the index of the image you are putting into it.
Basically, if you scroll quickly from 0-19 and the cell you want to update is #20 and you want it to show image #20, but it's still loading images 3, 7, 14 asynchronously.
To prevent this, what I did was track two indices; #1) the most recent indexpath that reflects the actual position of the cell and #2) the index corresponding to the image that is actually being loaded async (in this case should actually be the indexpath you are passing into cellforitematindexpath, it gets retained as the async process works through the queue so will actually be "old" data for some of the image loading) .
One way to get the most recent indexpath may be to create a simple method that just returns an NSInteger for the current location of the cell. Store this as currentIndex.
I then put a couple if statements that checked that the two were equal before actually filling in the image.
so if (currentIndex == imageIndex) then load image.
if you put an NSLog(#"CURRENT...%d...IMAGE...%d", currentIndex, imageIndex) before those if statements you can see pretty clearly when the two do not match up and the async calls should exit.
Hope this helps.
I found the wording of what chuey101 said, confusing. I figured out a way and then realized that chuey101 meant the same.
If it is going to help anyone, images are flashed and changed because of the different threads that are running. So, when you spawn the thread for image operations, its going to get spawned for a specific cell no, say c1. But, at last when you actually load your image into the cell, its going to be the current cell that you are looking at, the one that you scrolled to - say c2. So, when you scrolled to c2, there were c2 threads that were spawned, one fore each cell, as you scrolled. From what I understand, all these threads are going to try loading their images into the current cell, c2. So, you have flashes of images.
To avoid this, you need to actually check that you are loading the image that you want into the cell that you mean to load to. So, get the collectionviewcell indexpath.row before loading image into it (loading_image_into_cell). Also, get the cell for which you spawned off your thread to before you spawn off the thread i.e. in the main thread (image_num_to_load). Now, before loading, check that these two numbers are equal.
Problem solved :)
I have to 20-25 download images of 50 Kb- 2 Mb each and show them in a tableview.
I used ASIHTTPRequest asyn request to this. I observed that after some time the app gets stuck. This should not happen because I am using a async call. I thought something is wrong with ASIHTTPRequest and I observed that The didFinished selector gets called in the main thread. The only thing which I do is
-(void)didFinishedDownloadingImage:(ASIHTTPRequest*)request
{
NSData *responseData = [request responseData];
UIImage *image = [UIImage imageWithData:responseData];
[[data objectAtIndex:request.tag] setImage:image];
[self.tableView reloadData];
}
I don't think this should cause any problem. Also in cellforrowatindexpath I just do
- (UItableViewCell *)tableviewView:(UItableView *)tableview
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UserProfile * user = [data objecAtIndex:indexpath.row];
UITableViewCell *cell = [tableView
dequeueReusableCellWithReuseIdentifier:#"ProfileCell"
forIndexPath:indexPath];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewDefaultStyle];
}
NSString *fullname = [NSString stringWithFormat:#"%#\n%#",
user.firstname, user.lastname];
if(user.image != nil)
[cell.imageView setImage:user.image];
else{
[cell.imageView setImage:[UIImage imageNamed:#"placeholder.jpg"]];
}
[cell.label setText:fullname];
return cell;
}
But the app is slow and freezes for 1-2 sec which is a considerable amount of time.
I have seen apps which does this very smoothly. I tried using an image of fixed size 5Kb which has a very significance performance improvement with using the above code. I don't know why should that make a difference for big images in this case because all downloading is happening in other thread via ASIHTTP .
Please, replace your framework with AFNetworking.
You can simple use..
IImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[imageView setImageWithURL:[NSURL URLWithString:#"http://i.imgur.com/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder-avatar"]];
or... directly in TableViewCell
NSURL *url = [[NSURL alloc] initWithString:[movie objectForKey:#"artworkUrl100"]];
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeholder"]];
"In the second line, we tell the image view where the thumbnail is located by passing an NSURL and we pass in a placeholder image, which is shown as long as our request has not returned a response"
Thats all!
Here you have an tutorial about that http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_afnetworking/
It's easy to make assumptions about the root cause of a laggy/slow application. Instead of guessing, why don't you test your suspicions? Profile your application with the Time Profiler instrument. It'll tell you which methods and functions your application is spending the most time in.
Here are some ideas until you have a chance to profile:
You might consider downloading the full-res images and creating thumbnails in the background and then caching them in an NSCache object. You can also run [UIImage imageWithData:responseData]; in a background thread. It's thread-safe until the point at which it interacts with the view hierarchy.
Selectively reloading a single cell should be faster than reloading the entire tableview, especially one with lots of images. Furthermore if you're doing all of the networking and processing on a background queue, there's no reason scrolling the tableview should be slow. Can you show us your entire implementation of the -cellForRowAtIndexPath: method? You've mentioned that you think setImage: is your slow point because rendering is slow. If you reload a single cell, only one cell needs to be rendered. If you reload the entire tableview, every cell must be re-rendered.
I am loading an image to a table view cell, each cell has an image. I've adapter a couple tutorials to the code below, but I am still having slow down.
I am loading these images from the documents directory. Any tips or ideas on how to speed this process up?
Edit Revised Code:
Beer *beer = (Beer *) [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayBeerName.text = beer.name;
// did we already cache a copy of the image?
if (beer.image != nil) {
// good. use it. this will run quick and this will run most of the time
cell.beerImage.image = beer.image;
} else {
// it must be the first time we've scrolled by this beer. do the expensive
// image init off the main thread
cell.beerImage.image = nil; // set a default value here. nil is good enough for now
[self loadImageForBeer:beer atIndexPath:indexPath];
}
- (void)loadImageForBeer:(Beer *)beer atIndexPath:(NSIndexPath *)indexPath {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithContentsOfFile:beer.imagePath];
beer.image = image;
dispatch_sync(dispatch_get_main_queue(), ^{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.beerImage.image = image;
});
});
}
Your algorithm looks pretty good. You've avoided many of the typical pitfalls. If you're still having UI performance problems, I'd suggest a couple of things:
You should try caching your images in memory. You could use NSMutableArray or NSMutableDictionary, but at Best way to cache images on ios app? Caleb discusses the merits of the NSCache class, which simplifies the process. If you do cache images, make sure you respond to memory pressure and purge the cache if necessary. You can respond to didReceiveMemoryWarning or add yourself as an observer to the notification center's UIApplicationDidReceiveMemoryWarningNotification.
Make sure your cached images are thumbnail sized or else you'll always have a little stuttering in your UI (if you need a resizing algorithm, let us know) and it will use up memory unnecessarily;
When you dispatch your image update back to the main queue, you should do so asynchronously (why have that background queue hang around and tie up resources as it waits for the the block to be sent back to the main queue to finish ... this is especially an issue once you have a couple of images backed up during a fast scroll); and
When you dispatch back to the main queue, you should check to make sure cell you get from cellForRowAtIndexPath is not nil (because if cell loading logic gets too backed up (esp on slower devices), you could theoretically have the cell in question having scrolled off the screen and your algorithm could crash).
I use an algorithm very much like yours, with almost the same GCD structure (with the above caveats) and it's pretty smooth scrolling, even on older devices. If you want me to post code, I'm happy to.
If you're still having troubles, the CPU profiler is pretty great for identifying the bottlenecks and letting you know where you should focus your attention. There are some great WWDC sessions available online which focus on how to use Instruments to identify performance bottlenecks, and I found them to be very helpful to gain proficiency with Instruments.
Here is my code. In viewDidLoad, I initialize my image cache:
- (void)initializeCache
{
self.imageCache = [[NSCache alloc] init];
self.imageCache.name = #"Custom Image Cache";
self.imageCache.countLimit = 50;
}
And then I use this in my tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ilvcCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// set the various cell properties
// now update the cell image
NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved
UIImage *image = [self.imageCache objectForKey:imagename];
if (image)
{
// if we have an cachedImage sitting in memory already, then use it
cell.imageView.image = image;
}
else
{
cell.imageView.image = [UIView imageNamed:#"blank_image.png"];
// the get the image in the background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// get the UIImage
UIImage *image = [self getImage:imagename];
// if we found it, then update UI
if (image)
{
dispatch_async(dispatch_get_main_queue(), ^{
// if the cell is visible, then set the image
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (cell)
cell.imageView.image = image;
[self.imageCache setObject:image forKey:imagename];
});
}
});
}
return cell;
}
and
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[self.imageCache removeAllObjects];
}
As an aside, one further optimization that you might contemplate would be to preload your cached images in a separate queue, rather than loading images in a separate thread just-in-time. I don't think it's necessary, as this seems to be more than fast enough for me, but it's one more option to speed up the UI.
Not much you can do here for the initial load, you're about as fast as it gets.
If it's still too slow, try loading smaller images if you can.
A couple of things:
First, be careful with -imageWithContentsOfFile, it won't cache anything. You're taking the full hit every time you load the image, as opposed to -imageNamed that'll keep the image warm in some cache.
You can of course cache that in your domain object, but I'd personally strongly advice against that.
Your memory footprint is going to go through the roof, forcing you to implement your own cache expiration mechanism, while Apple has a very good image cache through -imageNamed.
I'd be surprised if you can do a better job than apple on all 3 family of devices :)
Then, you're breaking the flyweight pattern of the UITableView here:
dispatch_sync(dispatch_get_main_queue(), ^{
cell.beerImage.image = image;
beer.image = image;
[cell setNeedsLayout];
});
Ask the table view to give your the cell at a given index rather than capture the cell in the block: by the time the image is loaded, that cell instance might actually have been reused for another index path, and you'll be displaying the image in the wrong cell.
And no need for -setNeedsLayout here, just changing the image is enough.
Edit: whoops! I missed the obvious thing with images in table view. What size are your images, what size is the image view, and what is the content mode on the image?
If your images are of a very different size than the image view and you're asking the imageview to resize, this will happen on the main thread and you'll take a massive performance hit there.
Resize the image to the image view off thread, after loading (a quick google search will give you the core graphics code to do that).
The missing step is to update the model with the fetched image. As it is, you're doing a new load for every cell every time. The model is the right place to cache the result of the relatively expensive load. Can you add a Beer.image property?
Then, your config code would look like this:
Beer *beer = (Beer *) [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayBeerName.text = beer.name;
// did we already cache a copy of the image?
if (beer.image != nil) {
// good. use it. this will run quick and this will run most of the time
cell.beerImage.image = beer.image;
} else {
// it must be the first time we've scrolled by this beer. do the expensive
// image init off the main thread
cell.beerImage.image = nil; // set a default value here. nil is good enough for now
[self loadImageForBeer:beer atIndexPath:indexPath];
}
Moved the loader logic here for clarity ...
- (void)loadImageForBeer:(Beer *)beer atIndexPath:(NSIndexPath *)indexPath {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithContentsOfFile:beer.imagePath];
beer.image = image;
dispatch_sync(dispatch_get_main_queue(), ^{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.beerImage.image = image;
});
});
}
You can have a look on this question ,previously answered at stack overflow.
UIImage in uitableViewcell slowdowns scrolling table
or else try this code
- (void)configureCell:(BeerCell *)cell
atIndexPath:(NSIndexPath *)indexPath
{
Beer *beer = (Beer *) [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayBeerName.text = beer.name;
UIImage *image = [UIImage imageWithContentsOfFile:beer.imagePath];
cell.beerImage.image = image;
[cell setNeedsLayout];
}
I have three arrays and I have copied all these arrays into a single array. All these three arrays are arrays of dictionaries.All arrays has a field called picture, but that pictures is coming from different sources- URL in one array, data in other and files in the third one.
Say, Array1 has dictionaries with a key - picture and its loaded from NSURL.
Similarly, Array2 and Array3 has dictionaries with same key name - picture and loaded from ContentofFiles and NSData.
Now, I want to populate tableview, of course,m having Custom UITableViewCell, it has image view as its content view. To load that image, what should I do.
I was doing this thing..
NSURL *url = [NSURL URLWithString:[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"]];
cell.contactImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
But, this will crash if cell.contactImageView.image don’t receive image from NSURL.So, what should I do? Any help, will be appreciated
But,
all you is to check if the received image is null and if it is, then set a template photo image called no photo like a photo on facebook when no profile picture is selected
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
if (img)
cell.contactImageView.image = img;
else
cell.contactImageView.image = [UIImage imageNamed:#"no_photo.png"];
If these images are retrieved from the net, i would suggest not using [NSData dataWithContentsOfURL:].
You should be using a non blocking method, that loads images asynchronously. Using this method on numerous rows in a table will cause performance issues.
Here's my recommendation , use the SDWebImage Library . Easy to use, and even easier to install.
Once you add the library to your project, simply #import the UIImageView+WebCache.h class usage example is below.
[cell.contactImageView.image setImageWithURL:[NSURL URLWithString:[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
You can add one more key in dictionary which will specify from where to take photo and accordingly you can provide image to cell.contactImageView, if you want to provide image from different sources.
Thanks a lot buddies for your Quick reply,especially #skram, due to your suggestion,my tableview performance has increased much. the Question, that I have asked,the better answer that I have thought of is using iskindofClass. If image is coming from any class, on a condition ,we can check from where that image was coming and populate our image accordingly.
if ([[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"] isKindOfClass:[UIImage class]])
{
cell.contactImageView.image = [[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"];
}
else if ([[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"] isKindOfClass:[NSString class]])
{
cell.contactImageView.image = [[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"];
}
else if([[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"] isKindOfClass:[NSURL class]])
{
[cell.contactImageView setImageWithURL:[NSURL URLWithString:[[contactList objectAtIndex:indexPath.row] objectForKey:#"picture"]]placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
Now, I’m able to populate the tableview properly.Once again thanx to scram.
I have a UITableViewCell that contains 4 photos and i get these photos from the web but the problem is when i scroll down the UITableView these photos are downloaded again
And this is the code:
ITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellPhoto"];
if (cell == nil) {
NSArray *nibObject = [[NSBundle mainBundle] loadNibNamed:#"CustomCellThumbnails" owner:self options:nil];
cell = [nibObject objectAtIndex:0];
}
// Get the photos
int getPhotos = indexPath.row * 4;
for (int i = 1; i <= 4; i++) {
if (getPhotos < [imageArray count])
{
UIButton *imageButton = (UIButton*)[cell viewWithTag:i];
NSString *url = [imageArray objectAtIndex:getPhotos];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.siteweb.com%#",url]]];
UIImage *imageFieldProfile = [[UIImage alloc] initWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
// Set the phoyo to the UIButton
[imageButton setBackgroundImage:imageFieldProfile forState:UIControlStateNormal];
[imageFieldProfile release];
// Set the corner of UIButton
[imageButton.layer setCornerRadius:5.0];
[imageButton.layer setMasksToBounds:YES];
imageButton.tag = getPhotos;
});
});
[imageButton addTarget:self action:#selector(displayPhoto:) forControlEvents:UIControlEventTouchUpInside];
}
getPhotos ++;
}
You should use the view controller to fill the cells, not the UITableViewCell. If you do that, it's not only a better coding style, it's also easier to save the data.
Anyway, if you really must: initialize the UITableViewCell with some kind of storage table, so that you can store the data you downloaded: rewrite initWithStyle:reuseIdentifier: to initWithStyle:reuseIdentifier:usingCacheTable:
But, again, the correct way to do this is to load the data in the view controller and let the UIView subclasses simply only show stuff.
You should save or cache the images in another object when they are downloaded, not in the table view cell. Perhaps using some sort of data source or model object from which the table view cell requests the images, instead of having the table view cell directly make any URL requests. Then the model object can cache images after downloading and before handing them to the cell.
You could use a combination of lazy loading image views in combination with local caching. This would be rather easy to accomplish using ASIHTTPRequest in combination with correctly setup caching flags.
ASIHTTPRequest is extremely easy to use and its caching is very well controllable.
In contrast to the other solutions suggested, I would stick to use UITableView and its UITableViewCells as this will allow you to use de/queued cells without having to build such logic yourself.
I have used such solution for a major newsmagazine app (over 2mio downloads) and am totally satisfied with the results.