I am trying to display some images from URL into my collection view. But when i try to do so, my images get displayed on collection view but they randomly keep refreshing and reloaded. Also when i try to scroll the collectionView, it again reloads the whole data, which i don't want. I just want to display images like a preview to user. Here below is my code to fetch the images from URL and display it on collection View.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
__block NSString *filename;
for(NSDictionary *item in [jsonSD objectForKey:@"Data"]) {
for (int i=0; i<=[[item objectForKey:@"Count"]integerValue]; i++) {
filename=[NSString stringWithFormat:@"http://..URL../media/content/%@%@%d_1.png",_getstring,[item objectForKey:@"subcategory_id"],i];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:filename];
[self downloadImageWithURL:url completionBlock:^(BOOL succeeded, NSData *data) {
if (succeeded) {
iv.image = [[UIImage alloc] initWithData:data];
}
}];
});
}
}
return cell;
}
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSData *data))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
completionBlock(YES, data);
} else {
completionBlock(NO, nil);
}
}];
}
Here 'iv' is my imageView, where i display the images on cell of collection view. I tried searching related contents but i didn't got any idea for above problem.
Please help me. Any help is appreciated.
So seems like there's two issues here:
The first problem is with:
cellForItemAtIndexPath
gets called every single time the cell appears on the screen, which means if you're scrolling then downloadImageWithURL
is going to get called every single time a cell appears on the screen.
The second issue seems to be that multiple images are being loaded for each cell with for (int i=0; i<=[[item objectForKey:@"Count"]integerValue]; i++)
This loop seems to download multiple images and then sets them in turn on the same cell?
Some solutions to the first issue:
viewDidLoad
and update the datasource every single time one of the images has downloaded.To the second problem:
Unsure of what the loop is meant to achieve here, do you want each image from the loop to be displayed in it's own collection view cell?
Updated:
Here's a quick example to show you how the code might look. I have no idea what's contained in jsonSD
or some of the other parts of your code, but it should give you the right idea:
@interface TestCollectionViewController () {
NSMutableArray *datasource;
NSDictionary *jsonSD;
}
@end
@implementation TestCollectionViewController
static NSString *const reuseIdentifier = @"Cell";
- (void)viewDidLoad {
[super viewDidLoad];
// We want to load the data from all cells as soon as the view loads, instead of loading the data as the cells scroll.
// Lazy loading cells will require a more advanced implementation
[self loadDatasource];
}
- (void)loadDatasource {
for (NSDictionary *item in [jsonSD objectForKey:@"Data"]) {
// Fill the datasource with null objects which match the total number of objects you want to display
datasource = [NSMutableArray arrayWithCapacity:[[item objectForKey:@"Count"] integerValue]];
for (int i = 0; i <= [[item objectForKey:@"Count"] integerValue]; i++) {
[datasource addObject:[NSNull null]];
}
// Reload the collectionview now so that the empty cells will be displayed with no image.
[self.collectionView reloadData];
// Load each image individually and replace the corresponding NSNull in the datasource.
for (int i = 0; i <= [[item objectForKey:@"Count"] integerValue]; i++) {
NSString *filename = [NSString stringWithFormat:@"http://..URL../media/content/%@%@%d_1.png", _getstring, [item objectForKey:@"subcategory_id"], i];
NSURL *url = [NSURL URLWithString:filename];
[self downloadImageWithURL:url completionBlock:^(BOOL succeeded, NSData *data) {
if (succeeded) {
[datasource replaceObjectAtIndex:i withObject:[UIImage imageWithData:data]];
// Reload the collectionview after each item has downloaded so the image gets displayed in the cell.
[self.collectionView reloadData];
}
}];
}
}
}
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSData *data))completionBlock {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
completionBlock(YES, data);
} else {
completionBlock(NO, nil);
}
}];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
// Only displaying one section
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return datasource.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
if ([datasource objectAtIndex:indexPath.row] == [NSNull null]) {
// Display a default image here
} else {
// Image has been loaded, display it in the imageview
iv.image = [datasource objectAtIndex:indexPath.row];
}
return cell;
}
Update 2:
The answer above isn't terrible efficient and won't work too well if you have hundreds of items. For a more solid implementation, i'd recommend using a library like SDWebImage to handle it for you. Then you can do something as simple as:
[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {... completion code here ...}];