Refactoring uitableviewcell async image loading - ios

So i have problem...For me, Having the dispatch_async code within cellForRowAtIndexPath method is too clunky and messy for me.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *CellIdentifier = #"myCellID";
myCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// someData - an array of dictionary for the tableview datasource
NSURL * imageURL = [NSURL URLWithString:someData[indexPath.row][#"photoURL"]];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.myImageView.image = image;
});
});
}
So after a little research my solution to this was:
-(void)loadAsyncImage:(NSIndexPath *)indexPath{
NSURL * imageURL = [NSURL URLWithString:someData[indexPath.row][#"photoURL"] ];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *myImage = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
// _myTableView a property link from the interface builder
myCell * cell = (id)[_myTableView cellForRowAtIndexPath:indexPath];
cell.myImageView.image = myImage;
});
});
}
and then in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *CellIdentifier = #"myCellID";
myCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self loadAsyncImage: indexPath];
}
To my surprise it works and is so much more cleaner...but... i dont know if this is the right way to do this. Is there a better approach to this issue? or is this ok?

Related

Smooth scrolling of UITableView

I have a smooth scrolling problem with the UITableView. Can anyone help me where is the problem? The code I have written is this-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomTableCell";
LabTestCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}//cell.txtHeading.text=#"hello";
cell.txtHeading.text=[[_ResponseDic valueForKey:#"title"]objectAtIndex:indexPath.row];
cell.txtdescribtion.text=[[_ResponseDic valueForKey:#"description"]objectAtIndex:indexPath.row];
cell.txtPrice.text=[[_ResponseDic valueForKey:#"purchase_price"]objectAtIndex:indexPath.row];
cell.txtLabName.text=[[_ResponseDic valueForKey:#"brand"]objectAtIndex:indexPath.row];
NSString *path=#"https://www.fingerfry.com/pharmgo/uploads/product_image/";
NSString *url=[path stringByAppendingString:[[_ResponseDic valueForKey:#"main_image"]objectAtIndex:indexPath.row]];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
cell.image.image=image;
return cell;
}
If you load the image in cellForRowAtIndexPath, use Asynchronous method backgroundly load the images, use this below code,
dispatch_queue_t image_queue = dispatch_queue_create("com.company.app.imageQueue", NULL);
dispatch_queue_t main_queue = dispatch_get_main_queue();
NSString *path=#"https://www.fingerfry.com/pharmgo/uploads/product_image/";
NSString *url=[path stringByAppendingString:[[_ResponseDic valueForKey:#"main_image"]objectAtIndex:indexPath.row]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
NSString *dataPath = [cachePath stringByAppendingPathComponent:url];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ( [fileManager fileExistsAtPath:dataPath] )
{
UIImage *image = [UIImage imageWithContentsOfFile:dataPath];
[cell.imgView setImage:image];
}
else
{
[cell.imgView setImage:[UIImage imageNamed:#"images.png"]];
dispatch_async(image_queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
UIImage *image = [UIImage imageWithData:imageData];
[UIImagePNGRepresentation(image) writeToFile:dataPath atomically:YES];
dispatch_async(main_queue, ^{
[cell.imgView setImage:image];
});
});
}
I have the same issue, i had put the above code in cellForRowAtIndexPath, its working normal scrolling, hope its helpful
First install the pod:
pod 'SDWebImage/WebP', '~>3.7' in pod file.
Then you have to import:
#import <SDWebImage/UIImageView+WebCache.h>
Write below code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomTableCell";
LabTestCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}//cell.txtHeading.text=#"hello";
cell.txtHeading.text=[[_ResponseDic valueForKey:#"title"]objectAtIndex:indexPath.row];
cell.txtdescribtion.text=[[_ResponseDic valueForKey:#"description"]objectAtIndex:indexPath.row];
cell.txtPrice.text=[[_ResponseDic valueForKey:#"purchase_price"]objectAtIndex:indexPath.row];
cell.txtLabName.text=[[_ResponseDic valueForKey:#"brand"]objectAtIndex:indexPath.row];
[cell.image sd_setImageWithURL:[NSURL URLWithString:[obj valueForKey:IMAGE_KEY]] placeholderImage:nil options:SDWebImageRefreshCached completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
}];
return cell;
}
Reference link :
https://github.com/rs/SDWebImage

UICollection View cell images are changing while scrolling horizontally

I am using array of url's for images to load in collection view. Images are changing while scrolling the collection view. How to fix this issue?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"RecentProductCell";
RecentProductCell *recentCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
recentCell.recentProductImg.image = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString *imageurl = [[latestProducts valueForKey:#"image"]objectAtIndex:indexPath.item];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageurl]];
dispatch_sync(dispatch_get_main_queue(), ^{
recentCell.recentProductImg.image = [UIImage imageWithData:imageData];
});
});
}
Replace dispatch_sync(dispatch_get_main_queue(), ^{
recentCell.recentProductImg.image = [UIImage imageWithData:imageData];
});
with
dispatch_sync(dispatch_get_main_queue(), ^{
if ( imageData )
{
UIImage *urlImage = [[UIImage alloc] initWithData:imageData];
RecentProductCell *recentCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (recentCell)
[recentCell.recentProductImg setImage:urlImage];
}
});
Let me know if this works for you.
cell.imageView.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSString *imageurl = [[latestProducts valueForKey:#"image"]objectAtIndex:indexPath.item];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageurl]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.imageView.image = image;
[cell setNeedsLayout];
}
});
}
});
Write this code in cellForRowAtIndexPath method of collectionView

Xcode table view lag when scrolling

(void)bilgileriYukle
{
NSMutableArray *yemekIsimleri = [[NSMutableArray alloc] init];
NSMutableArray *resimIsimleri = [[NSMutableArray alloc] init];
NSURL *myURL = [[NSURL alloc]initWithString:#"http://www.sevgilezzeti.com/WebServices/tarifler.php"];
NSData *myData = [[NSData alloc]initWithContentsOfURL:myURL];
if (myData == nil)
{
NSLog(#"INTERNET YOK YADA VERI CEKEMEDI");
}
else
{
NSLog(#"INTERNET VAR VERI CEKILDI");
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:myData options:kNilOptions error:nil];
for (id element in jsonArray)
{
NSString *durum = [element objectForKey:#"durum"];
if([durum isEqualToString:#"1"])
{
[yemekIsimleri addObject:[element objectForKey:#"yemekadi"]];
NSString *gelenYemekAdi =[element objectForKey:#"kapakresmi"];
NSString *imagePathKapak = #"http://sevgilezzeti.com/Admin/yemekresimkapak/";
combined = [NSString stringWithFormat:#"%#%#", imagePathKapak, gelenYemekAdi];
[resimIsimleri addObject:combined];
}
}
}
_TarifAdi=yemekIsimleri;
_ResimAdi=resimIsimleri;
}
///////////////////////////////////////////////////////////////////////////////////////
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TableViewCell" forIndexPath:indexPath];
NSUInteger row = [indexPath row];
cell.TitleLabel.text = _TarifAdi[row];
NSCache *_imageCache;
UIImage *image = [_imageCache objectForKey:#"DenemeRes"];
if (image == nil) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:_ResimAdi[row]]];
image = [UIImage imageWithData:data];
[_imageCache setObject:image forKey:#"DenemeRes"];
}
cell.ThumbImage.image = image;
return cell;
}
I can get images from URL and can see images but when I scroll the table view, it's very slow and lagging. How can I fix this problem ?
It's pretty easy and straightforward to load images synchronously in table view cells, however it's evil, because it would cause the lag when you scroll it, because it's actually trying to load and display the images as you're scrolling.
Therefore, you must avoid loading images synchronously, but asynchronously instead, using a different thread other than the main thread, so that the scrolling and loading + displaying images can be done dependently, avoiding any kind of lag.
This is possibly a duplicate question, since it's already been asked and there are tutorials out there, but since I myself always had a problem with this on my first iOS app and found it very confusing, I'm posting the answer here, hoping it's helpful.
Try adding something like this to your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath:
// 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];
});
});
dataWithContentsOfURL is blocking your main thread, that'w why the table is acting like it is. You should load images in back thread so that the main thread can handle UI without interruptions
NSURL *url = [NSURL URLWithString:link];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url cachePolicy:0 timeoutInterval:kTimeoutInterval];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
// set to ImageView
cell.ThumbImage.image = image;
}];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TableViewCell" forIndexPath:indexPath];
cell.tag = indexPath.row;
cell.imageView.image = nil;
// Rounded Rect for cell image
CALayer *cellImageLayer = cell.imageView.layer;
[cellImageLayer setCornerRadius:35];
[cellImageLayer setMasksToBounds:YES];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^(void) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:_ResimAdi[indexPath.row]]];
UIImage *image = [[UIImage alloc] initWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
CGSize itemSize = CGSizeMake(70, 70);
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[cell setNeedsLayout];
}
});
}
});
cell.TitleLabel.text = _TarifAdi[indexPath.row];
return cell;
}

PFQueryTableViewController Parse. Not loading image

Hi I am trying the code bellow and I can't get my image showing
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
cell.textLabel.text = [object objectForKey:#"venueName"];
cell.imageView.file = [object objectForKey:#"image"];
[cell.imageView.file getDataInBackgroundWithBlock:^(NSData *data, NSError *error){
cell.imageView.image = [UIImage imageWithData:data];
}];
return cell;
}
I also try:
[cell.imageView loadInBackground];
and nothing. Do I need to insert manually an UIImage in the cell?
Regards
To load a image from Parse.com (using PFImageView), you just do it:
creature.file = (PFFile *)file;
[creature loadInBackground];
Are you sure that the property "image" is a file? If it is, try this:
PFFile *theImage = [object objectForKey:#"image"];
[theImage getDataInBackgroundWithBlock:^(NSData *data, NSError *error){
NSData *imageFile = [theImage getData];
cell.imageView.image = [UIImage imageWithData:imageFile];
}];

How will I stop repeating the loading of images in my table view?

Here is my code displaying images from server. After the image is loaded, when you scroll down and then back, the image disappears and there's another delay before it's loaded again.
dispatch_async(kBgQueue, ^{
NSString *profileImageStr = [[displayItems objectAtIndex:indexPath.row] objectForKey:#"image_url"];
NSURL* profileImgURL = [NSURL URLWithString:profileImageStr];
NSData *profileImgData = [NSData dataWithContentsOfURL:profileImgURL];
NSString *postPhotoStr = [[displayItems objectAtIndex:indexPath.row] objectForKey:#"image_url"];
NSURL *postImgURL = [NSURL URLWithString:postPhotoStr];
NSData *postImageData = [NSData dataWithContentsOfURL:postImgURL];
dispatch_async(dispatch_get_main_queue(), ^{
if (postImageData && profileImgData != nil) {
cell.thumbnailImg.image = [UIImage imageWithData:profileImgData];
cell.bgImgView.image = [UIImage imageWithData:postImageData];
}else
{
cell.thumbnailImg.image = [UIImage imageNamed:#"noimage.jpg"];
cell.bgImgView.image = [UIImage imageNamed:#"noimage.jpg"];
}
});
}
);
return cell;
I think you need to implement cache for image. Store image in NSCache.
//instance
NSCache *imageCache;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *simpleTableIdentifier=nil;
if([objRepresentative.team count]>0)
{
simpleTableIdentifier=[NSString stringWithFormat:#"%#",[objRepresentative.employeeID objectAtIndex:indexPath.row]];
}
else
simpleTableIdentifier = #"ContactTableCell";
ContactTableCell *cell = (ContactTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ContactTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
NSString *photoString=[NSString stringWithFormat:#"http://www.foo.com/%#",[searchPhoto objectAtIndex:indexPath.row]];
UIImage *image = [imageCache objectForKey:photoString];
if (image)
{
cell.ProfilePicture.image=image;
}
else
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:[photoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:url]];
if(image)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *small = [self imageWithImage:image scaledToSize:CGSizeMake(100, 100)];
cell.ProfilePicture.image=small;
});
[imageCache setObject:image forKey:photoString];
}
else
cell.ProfilePicture.image=[UIImage imageNamed:#"noPicture.png"];
});
}
}
return cell;
}

Resources