UICollectionView Scroll Freeze with NSData (imageView) - ios

I've UICollectionView with UITableView in the same page. I'm using SDWebImage with UITableView and it is working fine. I'm trying to use SDWebImage with UICollectionView but couldn't make it. So I have used NSData but it is freezing with it. How I can solve this issue?
UPDATE: Is there a way to use SDWebImage with UICollectionView?
My code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"cell";
ProductsCollectionViewCell *pCell = [_collection dequeueReusableCellWithReuseIdentifier:simpleTableIdentifier forIndexPath:indexPath];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
_imgCollection = (UIImageView *)[pCell viewWithTag:100];
_imgCollection.image = [UIImage imageWithData:imageData];
pCell.layer.shouldRasterize = YES;
pCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
}
My code for table view
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"productCell";
ProductsTableViewCell *pCell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
[pCell.imgProduct sd_setImageWithURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];

The method you are using
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
is synchronous, which means it will block the thread until it's done. (That's why it's freezing)
I recommend you figure out a way to use SDWebImage with your code, if you tell us why that didn't work, we can probably help you with that.
EDIT:
Look at the first example from SDWebImage's documentation, specifically at this line:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Now use it instead of this line of your code:
_imgCollection.image = [UIImage imageWithData:imageData];
And also, get rid of the line where you are downloading the image.

Could it be the fact that
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL ...
is performing a time consuming operation on the main thread?
Why not try putting the fetch in the background like so?
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL* url = [NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]
NSData *imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
UICollectionViewCell* cell = [collectionView cellForItemAtIndexPath:indexPath];
if(cell) { // cell is visible
cell.imageView.image = [UIImage imageWithData:imageData];
}
});
});
Finally, make sure that
[_arrayImage objectAtIndex:indexPath.row]
is indeed a string and that _arrayImage is a string array
Lastly I would consider putting
[UIImage imageWithData:imageData]
in the background as well as the decoding could be time consuming as well

May be you are missing contentView
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"cell";
ProductsCollectionViewCell *pCell = [_collection dequeueReusableCellWithReuseIdentifier:simpleTableIdentifier forIndexPath:indexPath];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
_imgCollection = (UIImageView *)[pCell.contentView viewWithTag:100];
_imgCollection.image = [UIImage imageWithData:imageData];
}

Related

Load and display image one by one from URL and Cache

I am using SDWebImage library to display images in my scrollview with page control.
How to download image one by one and display it. Suppose i have 4 URL : url1,url2,url3,url4
so i want to display url1 first then...2,3,4..so on...2,3,4 should load in background until the are not download a placeholder image show there.
Below is my code of inside for loop i try is it perfect ?
NSString* serverImgicon=<url here in a loop>;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url=[NSURL URLWithString: serverImgicon];
//set your image on main thread.
dispatch_async(dispatch_get_main_queue(),^{
[img sd_setImageWithURL:url
placeholderImage:[UIImage imageNamed:#"placeholderImg"]];
});
});
Try below code, it'll work fine
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionViewCell *cell =[collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
myobject *newobject = _myarray[indexPath.item];
cell.nameLabel.text =newobject.name;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[cell.activityIndicatorForImage startAnimating];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
[NSURL URLWithString:newobject.image]]];
dispatch_sync(dispatch_get_main_queue(), ^{
if(image!= nil){
[[cell myimage] setImage:image];
[cell.activityIndicatorForImage setHidden:NO];
}else{
UIImage * image = [UIImage imageNamed:#"PLACEHOLDER"];
[[cell myimage] setImage:image];
[cell.activityIndicatorForImage setHidden:YES];
[cell.activityIndicatorForImage stopAnimating];
}
[cell setNeedsLayout];
});
});
return cell;
}
use sdwebCache to load images asynchronously
https://github.com/rs/SDWebImage
/******************/
#import "UIImageView+WebCache.h"
then load images like this :
NSArray *tempArray = [stickersThumbRecords objectForKey:collectioName];
if (tempArray.count>indexPath.item) {
NSURL *url = [NSURL URLWithString:[tempArray objectAtIndex:indexPath.item] ];
[imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeholderFrame"]];
}

Collection view images are flickering while scrolling

Im using the following code to populate a collection view cells with images.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = nil;
if ([imageArray count] >0){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData: data0];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
recipeImageView.image = image;
});
});
}
[spinnerShow stopAnimating];
return cell;
}
The problem is that, when Im scrolling the images are flickering and are flashing. Why is that so? How can I make those images to be stable without flickering?
Just a short overview, So you get your answer
UICollectionView is highly optimized, and thus only keep On-screen visible rows in memory. Now, All rows Cells are cached in Pool and are reused and not regenerated. Whenever, user scrolls the UICollectionView, it adds the just-hidden rows in Pool and reuses them for next to be visible rows.
So, now, coming to your answer
When you scroll your CollectionView, collectionView datasource method gets called again for every indexPath, coming in visible range and your image gets downloaded again
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
SOLUTION
Instantiate a instance NSMutableDictionary, outside of method.
Now in your code
#implementation ClassName{
NSMutableDictionary *cachedImage;
}
-(void)viewDidLoad(){
[super viewDidLoad];
cachedImage = [NSMutableDictionary new];
}
/*OLD CODE*/
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = nil;
if ([imageArray count] >0){
//IF image is already downloaded, simply use it and don't download it.
if(cachedImage[[imageArray objectAtIndex:indexPath.row]] != nil){
recipeImageView.image = cachedImage[[imageArray objectAtIndex:indexPath.row]];
}
else{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData: data0];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
recipeImageView.image = image;
//****SAVE YOUR DOWNLOADED IMAGE
cachedImage[[imageArray objectAtIndex:indexPath.row]] = image; //****SAVE YOUR DOWNLOADED IMAGE
});
});
}
}
/*OLD CODE*/
as per my knowledge, you are fetching image but not caching it that's why when your UICollectionViewCell gets reload, you get UIImageView's fresh instance so and this thing goes on and on in your code..
in this case, i recommend you to use SDWebImage OR AFNetworking Frameworks. because these frameworks does all the tricky stuff for you with the simple line of code (SDWebImage Framework),
NSURL* url = [NSURL URLWithString:str];
[yourImageView setBackgroundImageWithURL:url forState:UIControlStateNormal placeholderImage:kPlaceholder];

collection view scrolling issue

collection view scrolling issue :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
photoFuncVal=1;
size=CGSizeMake(85, 85);
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
UIImageView *icon=(UIImageView *)[cell viewWithTag:100];
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[_responseAdvPicArray objectAtIndex:indexPath.row] ]]];
if (imgData)
{
//image = [UIImage imageWithData:imgData];
image = [[UIImage alloc] initWithData:imgData];
//icon.image = image;
scaledImage=[UIImage imageWithData:UIImageJPEGRepresentation(image, 4.0)];
scaledImage=[self imageWithImage:image convertToSize:size];
if (image)
{
dispatch_async(dispatch_get_main_queue(), ^{
//icon.image=scaledImage;
cell.backgroundColor=[UIColor colorWithPatternImage:scaledImage];
});
}
}
});
cell.layer.borderWidth=4;
cell.layer.borderColor=[[UIColor whiteColor]CGColor];
return cell;
}
the problem is that when the collection view is scrolled the images are reloaded and also duplicate at times
Register your cell like this :
static NSString *CellIdentifier = #"CollectionCell";
[self.collectionHome registerNib:[UINib nibWithNibName:#"CollectionViewCell" bundle:nil] forCellWithReuseIdentifier:#"CollectionCell"];
CollectionViewCell *cell ;
if(cell==nil)
{
cell = [cv dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
}
Please try this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
photoFuncVal=1;
size=CGSizeMake(85, 85);
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
**cell.backgroundColor = nil;**
UIImageView *icon=(UIImageView *)[cell viewWithTag:100];
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[_responseAdvPicArray objectAtIndex:indexPath.row] ]]];
if (imgData)
{
//image = [UIImage imageWithData:imgData];
image = [[UIImage alloc] initWithData:imgData];
//icon.image = image;
scaledImage=[UIImage imageWithData:UIImageJPEGRepresentation(image, 4.0)];
scaledImage=[self imageWithImage:image convertToSize:size];
if (image)
{
dispatch_async(dispatch_get_main_queue(), ^{
//icon.image=scaledImage;
cell.backgroundColor=[UIColor colorWithPatternImage:scaledImage];
});
}
}
});
cell.layer.borderWidth=4;
cell.layer.borderColor=[[UIColor whiteColor]CGColor];
return cell;
}

In a UICollectionView how can I preload data outside of the cellForItemAtIndexPath to use within it?

Figured out slow loading images were the behind the choppy slow effect of my collectionView.
I've been reading different Q&A's all day and various forum posts. It looks like the best way to solve this issue is to have the data pre-loaded available for the cellForItemAtIndexPath to be able to take what it needs.
I'm not sure how I can do this. I'm using parse as my backend, but sure if given a rough example I'd be able to figure out how to do it. From what I've seen so far I need a separate method to grab the data.
Here is the code:
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [[self objects] count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
NSArray *people = [self objects];
static NSString *CellIdentifier = #"Cell";
VAGGarmentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath:indexPath];
PFObject *current;
current = [people objectAtIndex:indexPath.item];
PFFile *userImageFile = current[#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
UIImage *image = [UIImage imageWithData:imageData];
[[cell contentView] setContentMode: UIViewContentModeScaleAspectFit];
[[cell imageView] setImage:image];
}];
[[cell title] setText:[current valueForKey:#"title"]];
[[cell price] setText:[NSString stringWithFormat: #"£%#", [current valueForKey:#"price"]]];
return cell;
}
So maybe the cellForItemAtIndexPath needs to call that method and take what it needs. Because the data would already be available it won't need to be loaded in the cellForItemAtIndexPath method and the cells will be populated immediately.
Please give suggestions and examples.
I was told a good way to do this would be to check for the image, if non existent provide a placeholder, if it does exist set it. Here are the changes to the above code.
Updates:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
NSArray *people = [self objects];
static NSString *CellIdentifier = #"Cell";
VAGGarmentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath:indexPath];
PFObject *current;
current = [people objectAtIndex:indexPath.item];
PFFile *userImageFile = current[#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error)
{
if (!image) {
[[cell imageView] setImage:[UIImage imageNamed:#"placeholder.png"]];
} else {
image = [UIImage imageWithData:imageData];
//resize image
CGSize destinationSize = CGSizeMake(158,187);
UIGraphicsBeginImageContext(destinationSize);
[image drawInRect:CGRectMake(0,0,destinationSize.width, destinationSize.height)];
//New image
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//Optimise image
NSData *imageDataCompressed = UIImageJPEGRepresentation(newImage, 0.4f);
// NSLog(#"Image Size %#", NSStringFromCGSize(newImage.size));//log size of image
NSLog(#"%#", [current valueForKey:#"title"]);
[[cell imageView] setImage:[UIImage imageWithData:imageDataCompressed]];
}
}
}];
[[cell title] setText:[current valueForKey:#"title"]];
[[cell price] setText:[NSString stringWithFormat: #"£%#", [current valueForKey:#"price"]]];
return cell;
}
Place holder shows fine but remains, how do I know when the image has been loaded so I can make my cells reflect that?
Thanks for your time.
Kind regards.
Update:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
NSArray *people = [self objects];
static NSString *CellIdentifier = #"Cell";
VAGGarmentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath:indexPath];
[cell.activityIndicator startAnimating];
PFObject *current;
current = [people objectAtIndex:indexPath.item];
PFFile *userImageFile = current[#"image"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:userImageFile.url, indexPath.item]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataDontLoad
timeoutInterval:6.0];
[cell.imageView setImageWithURLRequest:urlRequest
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
//resize image
CGSize destinationSize = CGSizeMake(158,187);
UIGraphicsBeginImageContext(destinationSize);
[image drawInRect:CGRectMake(0,0,destinationSize.width, destinationSize.height)];
//New image
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//Optimise image
NSData *imageDataCompressed = UIImageJPEGRepresentation(newImage, 0.4f);
cell.imageView.image = [UIImage imageWithData:imageDataCompressed];
NSLog(#"Image Size %#", NSStringFromCGSize(newImage.size));//log size of image
[cell.activityIndicator stopAnimating];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Failed to download image: %#", error);
}];
return cell;
}
Latest Update:
Set up a method that gets data from parse.com and stores in an NSMutableDictionary then in a mutable array. I store the title, price and URL to image of the garment.
- (void)grabDataFromCloud
{
self.model = [NSMutableArray array];
for (PFObject *object in [self objects]) {
PFFile *imageFile = [object valueForKey:#"image"];
NSURL *url = [NSURL URLWithString:imageFile.url];
NSMutableDictionary *newObject = [NSMutableDictionary dictionaryWithDictionary:#{#"title": [object valueForKey:#"title"], #"price": [object valueForKey:#"price"], #"imageUrl": url}];
[[self model] addObject:newObject];
}
}
This gets called in my cellForItemsAtIndexPath method.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
[self grabDataFromCloud];
static NSString *CellIdentifier = #"Cell";
VAGGarmentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath:indexPath];
[cell.activityIndicator setHidden:YES];
NSMutableDictionary* d = [self.model objectAtIndex:indexPath.item];
cell.title.text = d[#"title"];
cell.price.text = [NSString stringWithFormat:#"£%#", d[#"price"]];
if (d[#"image"]) {
cell.imageView.image = d[#"image"];
} else { // if not, download it
cell.imageView.image = nil;
dispatch_queue_t backgroundQueue = dispatch_queue_create("test", 0);
dispatch_async(backgroundQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:d[#"imageUrl"]];
UIImage* img = [UIImage imageWithData:data];
d[#"image"] = img;
dispatch_async(dispatch_get_main_queue(), ^{
//causes crash Assertion failure in -[UICollectionView _endItemAnimations],
// /SourceCache/UIKit/UIKit-2935.137/UICollectionView.m:3687
// [self.collectionView reloadItemsAtIndexPaths:#[indexPath]];
});
});
}
return cell;
}
I'd suggest you to use AFNetworking's UIImageView+AFNetworking category. It will handle the placeholder etc automatically, and will do everything in a background thread, ensuring that the main thread doesn't get blocked. Specifically, this is the method you'd want to call:
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage;
It is up to you to supply a placeholder image (or nil) when the image is first needed and to start downloading it, and then to hang on to the image once it has been downloaded so that ever after that you can supply it instantly. This example is for a table view, but the principle is exactly the same; the key thing is that my data model is a bunch of NSMutableDictionary objects, and each dictionary in not only the url for the picture we are supposed to have but also a place for keeping the image once it has been downloaded:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSMutableDictionary* d = (self.model)[indexPath.row];
cell.textLabel.text = d[#"text"];
if (d[#"im"]) { // if we have a picture, supply it
cell.imageView.image = d[#"im"];
} else if (!d[#"task"]) { // if not, download it
cell.imageView.image = nil;
NSURLSessionTask* task = [self.downloader download:d[#"picurl"]
completionHandler:^(NSURL* url){
if (!url)
return;
NSData* data = [NSData dataWithContentsOfURL:url];
UIImage* im = [UIImage imageWithData:data];
d[#"im"] = im;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
});
}];
}
return cell;
}
I suggest a different approach.
Google for - or use the search on SO - Asynchrnous loading. Nearly every app programmer faces this issue earlier or later. Consequentially there are tons of tutorials out there.
This is one of them.
http://www.markj.net/iphone-asynchronous-table-image/
I think it is older than the UICollectionView and therfore explains it for UITableView. Both data source delegates are so close to each other that you can easily adopt the solution to your collection.
There are smarter ways of acomplishing your goal. But I think tht this way is a good starting point. You may later want to refactor the solution once you got comforatble with the approach in general.
After several days the issue was my images were far too large. I had to resize them and this instantly solved my issue.
I literally narrowed things down and checked my images to find they were not being resized by the method I thought was resizing them. This is why I need to get myself used to testing.
I learnt a lot about GCD and caching in the past few days but this issue could have been solved much earlier.

Loading images for UITableViewCell from URL (need to load them asynchronously)

I have a custom class that parses through an XML and gets URL strings for images (which I store in an array). Then I want to retrieve those strings to load images and display each in a UITableViewCell. Here is my code:
- (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"];
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
cell.imageView.image = image;
return cell;
}
Obviously, when I scroll down the table, the app loads more images, and when the images are loading, the user can't do anything. It looks like the app froze. The images I want to load are not very large (about 8kb). Maybe there is a way I could load and store them in the background? I also want to give more freedom to the user, like scrolling down and viewing cells with text but without an image. Then force the app to load the images for the cells the user is currently viewing.
Is there any way to modify my code or any other solution for proper loading of images from URL strings?
Any advice would be appreciated, thanks.
Try to load the images asynchronous, so the main thread doesn't block.
Your code would look as follows:
- (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"];
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
});
return cell;
}
You can use the SDWebImage framework to load images asynchronously.
Please follow the link:
https://github.com/rs/SDWebImage
Just add the SDWebImage framework to your project and set the other linker flag '-ObjC', to use the framework.
Download link : https://github.com/rs/SDWebImage/releases
Use this for asynchronus way of loading imageview
- (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"];
//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, ^{
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
});
return cell;
}
Download this project and use asynimageveiw in your cell's imageview.. link
Use NSOperation and NSOperationQueue.A perfect solution for your problem.Use below link to see how it works.
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
Or
Use AFNetworking library It also provide a category over UIImageview that download image asynchronously.

Resources