Reputation:
Im trying to load an image from an url.
Following is my code..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@""];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:nil];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
dispatch_async (dispatch_get_main_queue(), ^{
NSData * storeImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:productImageArray[indexPath.row]]];
self.productImage.image = [UIImage imageWithData:storeImageData];
});
[cell.contentView addSubview:self.productImage];
return cell;
}
The problem is that,
How can I sort this out?
Upvotes: 1
Views: 4250
Reputation: 621
@MGR, you can use Dispatch async, but there was a library https://www.cocoacontrols.com/controls/hanekeswift, an excellent library. it will handle downloading images and it has a method to load the image in imageview.public
func hnk_setImageFromURL(URL: NSURL, placeholder: UIImage? = default, format: Haneke.Format<UIImage>? = default, failure fail: ((NSError?) -> ())? = default, success succeed: ((UIImage) -> ())? = default)
it also has the cache feature. so you can simply implement this function in cellForRow
. thats it.evertything will be handled by Haneke.
* PLEASE USE THIS FRAMEWORK * https://github.com/onevcat/Kingfisher
This brings better performance and customization than Hanekeswift
Upvotes: 1
Reputation: 1783
I feel this is the best approach for downloading images asynchronously. I will use below method for this task always.
In cellForRowAtIndexPath:
NSURL *imgURL = [[NSURL URLWithString:productImageArray[indexPath.row]];
[self downloadImageWithURL:imgURL
completionBlock:^(BOOL succeeded, UIImage *image) {
self.productImage.image=image;
}];
and add this method
- (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 )
{
data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
NSLog(@"Error in downloading image:%@",url);
completionBlock(NO,nil);
}
}];
}
Using this,even we can know better if there is any problem while downloading image.
Upvotes: 2
Reputation: 2598
Your are downloading the images synchronously on main UI thread, which is causing the screen to freeze.
Please follow below article to resolve the issue:
iOS: How To Download Images Asynchronously (And Make Your UITableView Scroll Fast)
and also answer to your second point:
You seem to be using a single imageView instantiated outside the cellForRowAtIndexPath method, and then you are trying to add it in every cell, which will cause the image to be removed from previous cell, and added in current cell
because a view could have only one superview, and once you try to add a child view in other view, it will get removed from its current super view
Upvotes: 0
Reputation: 376
Try this one:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = image;
Or sometimes what I do is to update the model and then reload the cell at the specific indexPath:
myModel.image = downloadedThumbImage;
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"top50places";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//getting the selected row image
NSDictionary* currentImageDictionary=[self.topfifty objectAtIndex:indexPath.row];//topFifty is an array of image dictionaries
UIImage *currentImage = [currentImageDictionary objectForKey:@"image"];
if (currentImage) {
// we already fetched the image, so we just set it to the cell's image view
cell.imageView.image = currentImage;
}
else {
// we don't have the image, so we need to fetch it from the server
// In the meantime, we can set some place holder image
UIImage *palceholderImage = [UIImage imageNamed:@"placeholder.png"];
cell.imageView.image = palceholderImage;
// set the placeholder as the current image to your model, so you won't
// download the image multiple times (can happen if you reload this cell while
// download is in progress)
[currentImageDictionary setObject:palceholderImage forKey:@"image"];
// then download the image
// creating the download queue
dispatch_queue_t downloadQueue=dispatch_queue_create("thumbnailImage", NULL);
dispatch_async(downloadQueue, ^{
UIImage *downloadedThumbImage=[self getImage:currentImageDictionary] ;
//Need to go back to the main thread since this is UI related
dispatch_async(dispatch_get_main_queue(), ^{
// store the downloaded image in your model
[currentImageDictionary setObject:image forKey:@"image"];
// update UI
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = image;
});
});
dispatch_release(downloadQueue);
}
return cell;
}
Upvotes: 0
Reputation: 1206
You can use GCD to load images in background thread, like this:
//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, ^{
NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:image];
});
});
But The simplest fix that addresses these issues is to use a UIImageView category, such as is provided with SDWebImage or AFNetworking. If you want, you can write your own code to deal with the above issues, but it's a lot of work, and the above UIImageView categories have already done this for you.
Upvotes: 0
Reputation: 3219
Try NSURLSessionTask
:
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell * cell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (cell)
cell. productImage.image = image;
});
}
}
}];
[task resume];
Upvotes: 0