I have added a table view, and I am display image in the cells. I have also added this code:
So that the cells resize depending on the image.
When I launch my app though, I get this : [![enter image description here][1]][1]
And the images do not load untill I start scrolling...If I scroll down half the page then go back to the top, I get this: Which is correct
[![enter image description here][2]][2]
Any ideas? I have researched on google and tried the odd solution for the older versions of Xcode, But nothing seems to work!
Here is the rest of my code from the TableViewController:
Image isn't loaded correctly in cellForRowAtIndexPath delegate method, you're (probably) downloading the image in the background, so cellForRowAtIndexPath is returned before image is ready.
Downloaded image is probably cached somewhere so next time it's loaded properly.
post.downloadImage() better have a callback closure to be called when image was downloaded, to assign the downloaded image into the proper cell.
Keep in mind that user may scroll this cell out of the screen before image is loaded, so you better use a unique id to abort downloaded image assignment if cell has already changed.
Here's an example for a method that downloads an image in the background, then assigns it to the cell -
+ (void)loadImage:(NSString *)imageUrl onComplete:(void(^)(UIImage *image, BOOL loaded, NSString *callIdentifier))callback callIdentifier:(NSString *)callIdentifier {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{
[self downloadPicture:url onComplete:^(UIImage *image, BOOL loaded) {
dispatch_sync(dispatch_get_main_queue(), ^{
callback(image, loaded, callIdentifier);
});
}];
});
callback([UIImage imageNamed:#"placeholder"], NO, callIdentifier);
}
+ (void)downloadPicture:(NSString *)url saveTo:(NSString *)filePath onComplete:(void (^)(UIImage *image, BOOL loaded))onComplete {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url] options:NSDataReadingMappedAlways error:&error];
if (!error) {
UIImage *image = [UIImage imageWithData:data scale:GTUserPictureScale];
if (onComplete)
onComplete(image, YES);
} else {
NSLog(#"Error loading user picture: %#", [error description]);
if (onComplete)
onComplete([UIImage imageNamed:#"missing"], NO);
}
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
__weak MyClass *wself = self;
self.imageUrl = #"http://...";
[self loadImage:self.imageUrl onComplete:^(UIImage *image, BOOL loaded, NSString *callIdentifier) {
#synchronized(wself) {
if ([callIdentifier isEqualToString:wself.imageUrl]) {
if (loaded) {
wself.image = image;
}
} else
NSLog(#"Expired load image request for id %#", callIdentifier);
}
} callIdentifier:self.imageUrl];
// ...
}
Related
Just to better give an idea of what this post is about, it ultimately ends out in this question:
How do I asynchronously download a not predefined number of images from any number of url's, add them to a dictionary (or array, if that's easier) so that they are added in the order the download is started, instead of adding them in the order they are finished?
This is the backbone question of this post, however for good measures and to actually allow one to understand what I mean, I have added my specific case involving this question in the following post. I know it's long, but it's pretty complex to explain my case.
So, here goes nothing:
I have a tableView which loads a couple of things from different arrays. All the arrays is created from a JSON fetch each time the app is launched, the idea being that I can update the info in my app by simply updating the JSON text file. One of the entries in the JSON text file contain url's to images, which I want to add to the tableView cell's contentview. I got the JSON fetch working, and a dictionary "dataDictionary" containing the info from the fetch is created.
The arrays then, get created like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Array is created from the dictionary created from the JSON fetch
_dataArray = _dataDictionary[#"games"];
// Arrays that will eventually hold info from JSON file is created
_Titles = [[NSMutableArray alloc] init];
_Descriptions = [[NSMutableArray alloc] init];
_Date = [[NSMutableArray alloc] init];
//images will be added to dictionary instead
_imageDict = [[NSMutableDictionary alloc]init];
// Info parsed from the JSON fetch, is now added to arrays
for (NSDictionary *eachData in _dataArray)
{
_Title = eachData[#"Title"];
_Description = eachData[#"Description"];
_Date = eachData[#"Release"];
// Key for image url is created
_image = eachData[#"ImageLink"];
[_Titles addObject:_Title];
[_Descriptions addObject:_Description];
[_Dates addObject:_Date];
Now there is more code below this, which is where I handle the images (will come just after this short explanation and below code sample), as you can see I have specified a key named "image" for the ImageLink entry in the JSON. Now I call this method, which starts an asynchronous download of the images:
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^) (BOOL succeeded, UIImage *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
This method is called in the below code, so to continue the code from before:
// Just to be clear, we are now back in the "for (NSDictionary *eachData in _dataArray)" statement from the first code sample
//calling the above method here
[self downloadImageWithURL:[NSURL URLWithString:_image] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
//All this code runs when an image is successfully downloaded
NSLog(#"image download succesfull");
UIImage *downloadedImage = [[UIImage alloc] init];
// Doing this so I can resize the downloaded image to a proper size
downloadedImage = image;
//long boring code to resize image here
UIImage *resizedImage = [[UIImage alloc] init];
// resizedImage is as the name implies, the resized image that now has a proper size for the cell's content view
_number++; //Int that start out as -1, this increments each time an image is downloaded to allow the images to be added to the dictionary with the keys 0, 1, 2...
// Wrapping the int in a NSNumber
NSNumber *number = [NSNumber numberWithInt:_number];
// Turning the NSnumber into a string
NSString *key = [number stringValue];
// Images finally being added to the dictionary, but, in the wrong "order" - or rather, with the wrong keys / numbers
[_imageDict setObject:resizedImage forKey:key];
//Update tableView when images are added to dictionary
if (_imageDict.count == _gameTitles.count) {
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
}
}];
//Update tableView when data is added to array, this is to allow info to be shown even before the images are done downloading
if (Titles.count == _dataArray.count) {
// Update tableView with loaded content
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
} // "for" statement ends here
}
To sum the most important parts up:
When filling the cell's in the tableView, I use the indexPath.row to get the proper info from each array.
The images download, and I add them to an dictionary with the keys 0, 1, 2.. when finished, however, because the download is asynchronous the download do not finish in the order it was initialized, rather, the smaller images get (not surprisingly) downloaded and added to the image dictionary first. This means that the keys of the images does not fit with the order in all my other arrays, so setting the image in the first cell with indexPath.row simply sets the image that was downloaded first, rather than the image download that was started first. Basically, even if image 5 gets downloaded first, it should be added with the key "4", and not "0".
I should also mention that because of the JSON fetch I do NOT know how many pictures will be downloaded beforehand as this can change depending on the JSON. This cancels out a lot of the answers here on stackoverflow (and other places as well).
So all this text and code and whatnot leads to the beginning question, how do I asynchronously download a not predefined number of images from any number of url's, add them to a dictionary (or array, if that's easier) so that they are added in the order the download is started, instead of adding them in the order they are finished?
Thank you very much for taking your time to read this.
So all this text and code and whatnot leads to the beginning question, how do I asynchronously download a not predefined number of images from any number of url's, add them to a dictionary (or array, if that's easier) so that they are added in the order the download is started, instead of adding them in the order they are finished?
You can use SDWebImage Library to download images,it asynchronously downloads your images from given URLs and it also caches images and manages all the things for you and it is really popular.All you have to do is to add below code to your -cellForRowAtIndexPath delegate method:
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:imageURLThatYouGetFromJSONResponse]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Also I recommend you not to use arrays or dictionaries.You can store that all information in an object.It is easy and best-practise in many cases and called as object-oriented approach.You create an NSObject subclass and in there create properties "title", "description", "name", "imageURL".You can follow this OOP Tutorial for some better understanding.
You don't care about number of images, because you define the number of rows in -numberOfRowsInSection and -cellForRowAtIndexPath is called the times you wrote in -numberOfRowsInSection.You can add:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrayOfYourObjects count];
}
Edit: How to manipulate images while using SDWebImage?
Answer: You can use SDWebImageManager, it also manages caching for you
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// Code to resize
//After resizing
cell.imageView.image = resizedImage;
}
}];
I'm storing 5 PFFiles in an array and using getDataInBackgroundWithBlock to download those files from Parse.
The problem is the order at which they appear in the table view cells is different every time, presumably because the files are download at different speeds due to the different file sizes.
for (PFFile *imageFile in self.imageFiles) {
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
[self.avatars addObject:avatar];
cell.userImageView.image = self.avatars[indexPath.row];
}
}];
}
The self.imageFiles array is in the correct order.
How do I ensure that the images downloaded are added to the self.avatars array in the same order as the self.imageFiles?
The question has two parts: (1) explicitly, how to maintain the order of results of asynchronous operations, (2) implied by the use of cell, how to properly handle asynch requests in support of a tableview.
The answer to the first question is simpler: keep the result of the request associated with the parameter for the request.
// change avatars to hold dictionaries associating PFFiles with images
#property(nonatomic,strong) NSMutableArray *avatars;
// initialize it like this
for (PFFile *imageFile in self.imageFiles) {
[avatars addObject:[#{#"pfFile":imageFile} mutableCopy]];
}
// now lets factor an avatar fetch into its own method
- (void)avatarForIndexPath:(NSIndexPath *)indexPath completion:^(UIImage *, NSError *)completion {
// if we fetched already, just return it via the completion block
UIImage *existingImage = self.avatars[indexPath.row][#"image"];
if (existingImage) return completion(existingImage, nil);
PFFile *pfFile = self.avatars[indexPath.row][#"pfFile"];
[pfFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[indexPath.row][#"image"] = avatar;
completion(avatar, nil);
} else {
completion(nil, error);
}
}];
}
Okay for part (1). For part 2, your cellForRowAtIndexPath code must recognize that cells are reused. By the time the asynch image fetch happens, the cell you're working on might have scrolled away. Fix this by not referring to the cell in the completion block (only the indexPath).
// somewhere in cellForRowAtIndexPath
// we're ready to setup the cell's image view
UIImage *existingImage = self.avatars[indexPath.row][#"image"];
if (existingImage) {
cell.userImageView.image = existingImage;
} else {
cell.userImageView.image = // you can put a placeholder image here while we do the fetch
[self avatarForIndexPath:indexPath completion:^(UIImage *image, NSError *error) {
// here's the trick that is often missed, don't refer to the cell, instead:
if (!error) {
[tableView reloadRowsAtIndexPaths:#[indexPath]];
}
}];
}
Reloading the row in the completion block will cause cellForRowAtIndexPath to be called again, except on that subsequent call, we'll have an existing image and the cell will get configured immediately.
Whilst danh's answer has answered my question, I did manage to solve it shortly after posting the question. I'm capturing the index of each imageFile and making sure they are added to the self.avatars array in that order.
for (PFFile *imageFile in self.imageFiles) {
NSInteger index = [self.imageFiles indexOfObject:imageFile];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[index] = avatar;
[self.tableView reloadData];
}
}];
}
Then cell.userImageView.image = self.avatars[indexPath.row]; in cellForRowAtIndexPath:
I'm currently using SDWebImage to load pictures for my table cells, using the following code:
[cell.coverImage sd_setImageWithURL:[self.dataInJSONModel.Content[indexPath.row] CoverImage] placeholderImage:[UIImage imageNamed:#"imageplaceholder_general"]];
The problem is when I scroll up and down, the images were inserted into the wrong cells. After reading some post on StackOverflow regarding this issue, I suspect it to be due to that cells are reused when we scroll and hence the asynchonous download of the image may be placed on a cell indexPath that has changed.
Hence I implemented several changes e.g.:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
UIImageView * cellCoverImage = cell.coverImage;
[manager downloadImageWithURL:[self.dataInJSONModel.Content[indexPath.row] CoverImage] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL * oriURL) {
NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
if ([visibleIndexPaths containsObject:indexPath]) {
cellCoverImage.image = image;
}
}];
Or even to compare URLs:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
UIImageView * cellCoverImage = cell.coverImage;
[manager downloadImageWithURL:[self.dataInJSONModel.Content[indexPath.row] CoverImage] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL * oriURL) {
if([oriURL isEqual:[self.dataInJSONModel.Content[indexPath.row] CoverImage]])
{
cell.coverImage.image = image;
}
}];
Still the problem persist. Or I might have wrongly programmed it? Found several suggestions online but no concrete solution yet seen.
Need help!
EDIT
I've already made some changes to it but still doesn't work:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NewsFeedCell * cell = [tableView dequeueReusableCellWithIdentifier:#"NewsFeedCell" forIndexPath:indexPath];
if (self.dataInJSONModel)
{
cell.coverImage.image = nil;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:[self.dataInJSONModel.Content[indexPath.row] CoverImage] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL * oriURL) {
if ([cell isEqual:[self.tableView cellForRowAtIndexPath:indexPath]])
{
cell.coverImage.image = image;
}
}];
}
I met the same problem, and tried assign .image = nil, but not work.
Finally, my sloution is to override prepareForReuse in UITableViewCell with cancel operation:
- (void)prepareForReuse
{
[super prepareForReuse];
[_imageView sd_cancelCurrentImageLoad];
}
Posted the question on the SDWebImage Github page and gotten a suggestion from someone who solves my problem! I just override the prepareForReuse method in my cell's implementation file and nullify the image of the affected imageView.
Sample code for future reader:
In my NewsFeedCell.m
- (void) prepareForReuse
{
[super prepareForReuse];
self.coverImage.image = NULL;
}
And this solves the problem! My opened issue at GitHub is https://github.com/rs/SDWebImage/issues/1024, should any of you want to see.
I tried most of these solutions, spent some time fixing this. I got 2 solutions working for me.
When setting image to ImageView in cells stop download.
In cellForRow add this:
cell.imageView.sd_cancelCurrentImageLoad()
and then in cell:
func prepareForReuse() {
imageView.image = UIImage.placeholderImage() // or nill
}
This is not real solutions because your actually stop image download and waste already downloaded data.
Another more elegant solutions is adding extension for UIImageView:
Choose animation which suits you, and try this:
func setImageAnimated(imageUrl:URL, placeholderImage:UIImage) {
self.sd_setImage(with: imageUrl, placeholderImage: placeholderImage , options:SDWebImageOptions.avoidAutoSetImage, completed: { (image, error, cacheType, url) in
if cacheType == SDImageCacheType.none {
UIView.transition(with: self.superview!, duration: 0.2, options: [.transitionCrossDissolve ,.allowUserInteraction, .curveEaseIn], animations: {
self.image = image
}, completion: { (completed) in
})
} else {
self.image = image
}
})
}
You are right in your analysis of the problem, just not executed it quite correctly.
some pseudocode for cellForRowAtIndexPath...
- set the cell.imageView.image to nil to wipe out
previous use of cell image view contents
- get the URL from data source
- initiate asynchronous download with completion ^{
//check the cell is still the correct cell
if ([cell isEqual: [collectionView cellForRowAtIndexPath:indexPath]]) {
cell.imageView.image = image
}
}
A couple of things you are doing wrong
- don't grab a reference to the cell's image view until you know you need it (in the completion block)
- don't check visibleIndexPaths, the indexPath might still be visible but allocated to a different cell (if you have scrolled off then on again for example). The 'cell isEqual' technique I use here should suffice for all cases.
You can also nil out old cell contents for a recycled cell by overriding the cells -prepareForReuse method.
The Obvious error here is that you're not accounting for using Blocks. Essentially, the completion happens on a background thread, and all UI updates must happen on the Main Thread.
The simple solution is;
dispatch_async(dispatch_get_main_queue(), ^{
cell.coverImage.image = image;
});
Further, if you intend to reference the tableView in your completion block, you should use a weak reference to it.
I have the following problem. I'm currently building a recipe-based app and use a standard tableview to display the recipes. On the app launch, a file is downloaded from a server which includes recipe data. The data is then entered into CoreData making sure, that it's unique. At the same time, I use SDWebImage in cellForRowAtIndexPath in order to load async. After downloading the images I save the images and save their paths in an Recipe object (it's an NSManagedObject). I also use UIRefreshControl in order to refresh the tableview in case there had been changes on the server (it uses the same mechanism as on app launch).
The issue with the code below is the saveImage function. The line recipe.thumbImage = [NSString...] causes issues with CoreData in that when I refresh the app using the pull-down gesture (thus activating UIRefreshControl), the recipes appear twice in the tableView. If I delete the line, the problems go away.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
NSString *imageURL = [#"http://www.example.com" stringByAppendingString:recipe.externalThumbImageURL];
[cell.thumbImageView setImageWithURL:[NSURL URLWithString:imageURL]
placeholderImage:[UIImage imageNamed:#"blankTableviewImage"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
if(error) NSLog(#"Error when downloading thumbImage, error: %#", error);
else {
[self saveImage:image forRecipeID:recipe.objectID];
}
}];
}
- (void)saveImage:(UIImage *)image forRecipeID:(NSManagedObjectID *)recipeID
{
if(image) {
ImageHandler *imageHandler = [[ImageHandler alloc] init];
NSError *error;
Recipe *recipe = (Recipe *)[self.recipeDatabase.managedObjectContext existingObjectWithID:recipeID error:&error];
NSString *fileName = [imageHandler getImageName:recipe.externalThumbImageURL];
NSString *localImageDirectory = [imageHandler imageDirectoryPathFromRecipe:recipe];
if (![[NSFileManager defaultManager] fileExistsAtPath:localImageDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:localImageDirectory withIntermediateDirectories:NO attributes:nil error:&error];
}
[imageHandler saveImage:image withFileName:fileName ofType:#"png" inDirectory:localImageDirectory];
recipe.thumbImage = [localImageDirectory stringByAppendingString:[NSString stringWithFormat:#"%#.png", fileName]];
if(error) NSLog(#"Error in saveImage – error: %#", error);
}
}
The function [cell.thumbImageView setImageWithURL:...] is part of SDWebImage and works async as far as I know. And I think that's part of the problem. And I have tried wrapping recipe.thumbImage in a [recipe.managedObjectContext performBlock:^{}] block, but it doesn't help either.
Does anyone have a hint as to what is the cause of the problem? I know that threading with CoreData is tricky and I've tried several things in order to make it work, but nothing has worked so far. Any hints are very much appreciated!
After some additional trial and error and reading I found out that it makes sense to create a child context when you do that kind of async updating. So in the saveImage method I now simply create a child context and wrap the recipe.thumbImage in a [context performBlock:^{}]. As I use UIManagedDocument with CoreData, you have to make sure to write the changes to the file.
Here's the code
- (void)saveImage:(UIImage *)image forRecipeID:(NSManagedObjectID *)recipeID
{
if(image) {
NSError *error;
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = self.document.managedObjectContext;
Recipe *recipe = (Recipe *)[context existingObjectWithID:recipeID error:&error];
...
[context performBlock:^{
recipe.thumbImage = [localImageDirectory stringByAppendingString:[NSString stringWithFormat:#"%#.png", fileName]];
[context save:nil];
[self.document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
}];
}
}
Here's also a good thread with more comments on multi-threading and core data and here an in-depth post
I have created a custom UITableViewCell which is composed by 2 UILabels and a single UIImageView.
Data associated with cells is available with a NSObject class named CellInfo. CellInfo has 2 properties of NSString type and an UIImage property.
When I create a CellInfo instance, inside the initWithData method (CellInfo class), I do the following:
if(self = [super alloc])
{
//initialize strings variables
self.name = aName;
self.descritpion = aDescription;
[self grabImage]
}
return self;
where grabImage (within CellInfo class) using ASIHTTPrequest framework to grab images in asynchronous manner (in the following code NSURL is alaways the same but in reality it changes with data)
- (void)grabImage
{
NSURL *url = [NSURL URLWithString:#"http://myurl.com/img.png"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
NSData *data = [request responseData];
UIImage* img = [[UIImage alloc] initWithData:data];
self.image = img;
[img release];
// Send a notification if image has been downloaded
[[NSNotificationCenter defaultCenter] postNotificationName:#"imageupdated" object:self];
}];
[request setFailedBlock:^{
NSError *error = [request error];
// Set default image to self.image property of CellInfo class
}];
[request startAsynchronous];
}
I have also a UITableViewController that loads data into the custom cell like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do stuff here...
// Configure the cell...
((CustomTableViewCell*)cell).nameOutlet.text = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).name;
((CustomTableViewCell*)cell).descriptionOutlet.text = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).descritpion;
((CustomTableViewCell*)cell).imageViewOutlet.image = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).image;
return cell;
}
In addiction, this UITableViewController observes notification from the CellInfo class because, at start up, images for visible cells are not displayed. This is the method that is called when the notification is captured:
- (void)imageUpdated:(NSNotification *)notif {
CellInfo * cInfo = [notif object];
int row = [self.infoArray indexOfObject:cInfo];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:0];
NSLog(#"Image for row %d updated!", row);
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
The code works well, but I would like to know if I'm doing right or there is a better way to do this.
My doubt is the following: is it correct to save downloaded images within each CellInfo instance or is it possible to follow another way to cache images using, for example, cache policy provided by ASIHTTPRequest?
P.S. grabImage is not called if the image for a specific CellInfo instance has already been downloaded.
I believe that's pretty neat. Instead of that you might subclass UIImageView class and create an initializer like [AsyncUIImageView initWithURL:] and then put that ASIHttpRequest logic inside the view.
After it finishes loading the picture, there could be two ways:
It can call [self setNeedsDisplay] (an UIView method) so image view is redrawn.
You can pass UITableViewCell or UITableView as a delegate to AsyncUIImgeView so that it could tell table view to reload that cell.