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;
}
Related
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];
Im using the following code to load images from url into collection view
-
(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
UILabel *recipeLabel = (UILabel *)[cell viewWithTag:200];
if ([ImageArray count] >0){
for(int i = 0; i < [ImageArray count]; i++)
{
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;
});
});
}
}else{
UILabel *title = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, cell.bounds.size.width, 40)];
title.text = #"No image record found";
title.tag = 200;
[title setHidden:true];
[cell.contentView addSubview:title];
}
[spinnerShow stopAnimating];
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
return cell;
}
If the ImageArray has got some url, if loop will be executed. Else the else loop will be executed where it will display a label saying no image record found. But the uilabel is not getting visible
Why are you using for loop in cellForItemAtIndexPath? This method is called for every item in UICollectionView. In else condition, add log or breakpoint and check if it is going there or always going in if condition. Make sure if you need to check for array count or want to check if image exists on URL?
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];
}
I am making an app in which i am getting data from server and in data image path is also coming but when i am setting image to my tableview cell app will become too much heavy may b i am not setting image properly below is my sample code thanx in advance :)
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *tableviewidentifier = #"cell";
tablecellTableViewCell *cell= [self.activitiesTableView_ dequeueReusableCellWithIdentifier:tableviewidentifier];
if(cell==nil)
{
cell = [[tablecellTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableviewidentifier];
}if(indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - 1){
// [[cell textLabel] setText:#"Load more records"];
}
UILabel *valuedate = (UILabel *)[cell viewWithTag:21];
UILabel *msg = (UILabel *)[cell viewWithTag:22];
UILabel *date = (UILabel *)[cell viewWithTag:23];
UILabel *time = (UILabel *)[cell viewWithTag:24];
valuedate.text=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerTitle"];
msg.text=#"How are you?";
NSString *img=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerPhoto"];// here i am getting image path
UIImage *img1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:img]]];
cell.imageView.image=img1;// here i am setting image due to which app is so heavy and stuck
return cell;
}
Dont use imageWithData: for setting images. It is synchronous and will make your app run slow.
Instead of that Use SDWebImage
You just need to do following things:
Dump SDWebImage folder into your project.
Import UIImageView+WebCache.h.
Set the image using: sd_setImageWithURL:
OR
by GCD (Grand Central Dispatch) and sending asynchronous requests. Code copied from HERE.
First implement following 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 )
{
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
and then in your cellForRowAtIndexPath
[self downloadImageWithURL:your_url completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
// change the image in the cell
cell.imageView.image = image;
}
}];
try this below code, hope this helps u .
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
__block tablecellTableViewCell *cell= [self.activitiesTableView_ dequeueReusableCellWithIdentifier:tableviewidentifier];
if(cell==nil)
{
cell = [[tablecellTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableviewidentifier];
}
if(indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - 1)
{
// [[cell textLabel] setText:#"Load more records"];
}
UILabel *valuedate = (UILabel *)[cell viewWithTag:21];
UILabel *msg = (UILabel *)[cell viewWithTag:22];
UILabel *date = (UILabel *)[cell viewWithTag:23];
UILabel *time = (UILabel *)[cell viewWithTag:24];
valuedate.text=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerTitle"];
msg.text=#"How are you?";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *img=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerPhoto"];// here i am getting image path
NSURL *url = [NSURL URLWithString:img];
NSData * imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_sync(dispatch_get_main_queue(), ^{ //in main thread update the image
cell.imageView.image = image;
cell.textLabel.text = #""; //add this update will reflect the changes
});
});
return cell;
}
EDIT
in order to reuse the downloaded image u can either save them on disk or just for save them some where for example in dictionary for temporary using
in below code i took one example dictionary, and strong the download images with row as key
#interface ViewController ()
{
NSMutableDictionary *imagesDictionary; //lets declare a mutable dictionary to hold images
}
in this method just initialise it
- (void)viewDidLoad {
[super viewDidLoad];
// rest of your code
//...........
//
imagesDictionary = [[NSMutableDictionary alloc]init]; //initilise
}
in index this method just add the downloaded images to dictionary for corresponding row as key
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
__block tablecellTableViewCell *cell= [self.activitiesTableView_ dequeueReusableCellWithIdentifier:tableviewidentifier];
if(cell==nil)
{
cell = [[tablecellTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableviewidentifier];
}
if(indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - 1)
{
// [[cell textLabel] setText:#"Load more records"];
}
__block NSString *row = [NSString stringWithFormat:#"%d",indexPath.row]; //add this
UILabel *valuedate = (UILabel *)[cell viewWithTag:21];
UILabel *msg = (UILabel *)[cell viewWithTag:22];
UILabel *date = (UILabel *)[cell viewWithTag:23];
UILabel *time = (UILabel *)[cell viewWithTag:24];
// valuedate.text=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerTitle"];
msg.text=#"How are you?";
if(![[imagesDictionary allKeys] containsObject:row]) //if image not found download and add it to dictionary
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// NSString *img=[[self.inboxmessagesarray objectAtIndex:indexPath.row]objectForKey:#"offerPhoto"];// here i am getting image path
NSURL *url = [NSURL URLWithString:img];
NSData * imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_sync(dispatch_get_main_queue(), ^{ //in main thread update the image
[imagesDictionary setObject:image forKey:row]; //sorry, while editing to your code i forgot to add this
cell.imageView.image = image;
cell.textLabel.text = #""; //add this update will reflect the changes
NSLog(#"loading and addig to dictionary");
});
});
}
else
{
cell.imageView.image = [imagesDictionary objectForKey:row];
NSLog(#"retriving from dictioary");
}
return cell;
}
First of all you are calling dataWithContentsOfURL: function which will make the app non responsive because you are calling it on main thread. To make it responsive you need to create a custom cell YourCell and declare a method in YourCell.h
#interface YourCell : UITableViewCell
{
UIImage *_cImage;
}
- (void)downloadImageFromURL:(NSURL *)imageUrl;
#end
Now in YourCell.m you need to do like this:
- (void)downloadImageFromURL:(NSURL *)imageUrl
{
if (_cImage != nil)
{
self.imageView.image = _cImage;
}
else
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_cImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageUrl]];
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = _cImage;
});
});
}
}
Now from cellForRowAtIndexPath: you just need to call downloadImageFromURL: function of YourCell and pass the imageUrl to it and its the cell responsibility to download and show the image.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier= #"YourCell";
YourCell *cell = (YourCell *)[self.activitiesTableView_ dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[NSBundle mainBundle] loadNibNamed:#"YourCell" owner:self options:nil] objectAtIndex:0];
}
// Set your UILabels as before.....
NSString *imagePath=[[self.inboxmessagesarray objectAtIndex:indexPath.row] objectForKey:#"offerPhoto"];
[cell downloadImageFromURL:[NSURL URLWithString:imagePath]];
return cell;
}
Let me know if you have any questions.
UIImageView *img1 = (UIImageView *)[cell viewWithTag:104];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
img1.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:img]]];
});
});
Send async requests for images. Doing this would not block your UI until the image gets loaded.
Im having problem setting image into UiCollectionViewCell
THis is my code :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSURL * imageURL = [NSURL URLWithString:#"https://mozorg.cdn.mozilla.net/media/img/firefox/firstrun/logo.png?2013-06"];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
UIImageView * myImageView = [[UIImageView alloc] initWithImage:image];
myImageView.image = [UIImage imageNamed:#"myimg"];//set the image
GLCell *cell = (GLCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_IDENTIFIER forIndexPath:indexPath];
cell.displayString = [NSString stringWithFormat:#"%d user number", indexPath.row];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"myimg"]];
return cell;
}
the string value shows without problem , but cell have no image.
NSURL * imageURL = [NSURL URLWithString:#"https://mozorg.cdn.mozilla.net/media/img/firefox/firstrun/logo.png?2013-06"];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
GLCell *cell = (GLCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_IDENTIFIER forIndexPath:indexPath];
cell.displayString = [NSString stringWithFormat:#"%d user number", indexPath.row];
cell.backgroundColor = [UIColor colorWithPatternImage:image]];
You aren't adding the myImageView to your cell. Instead of creating your own UIImageView you could just use the UITableViewCell's imageView property