I have a UICollectionView that holds 10 items (images retrieved from Parse.com) Every cell covers the entire screen and it is set as a Horizontal Scroll.
Inside the cell I have a 'Like Button' that when clicked it changes color. The problem is that if I click the like button in cell 1 then cell 3, cell 5, and so on also changes the buttons color.. and if I click on cell 0 then cell 2, cell 4 and so on changes it color also.
I tried changing the UICollectionView cells size and made is so that the 10 cells fit in the screen. When I did that, no buttons repeated. The only change was in the button I clicked. Any thoughts as why this is happening and how can I fix it? I need the Cells to be the entire Screen.
My Code:
VestimentaDetailViewController.m
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 10;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
VestimentaDetailCell *cell = (VestimentaDetailCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.progressView.hidden = NO;
[cell.progressView setProgress:0.02];
cell.imageFile.image = [UIImage imageNamed:#"loadingLook.png"];
PFFile *storeLooks = [self.vestimenta objectForKey:[NSString stringWithFormat:#"image_%ld", (long)indexPath.item]];
[storeLooks getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error && data.length > 0) {
cell.imageFile.image = [UIImage imageWithData:data];
} else {
cell.progressView.hidden = YES;
}
} progressBlock:^(int percentDone) {
float percent = percentDone * 0.02;
[cell.progressView setProgress:percent];
if (percentDone == 100){
cell.progressView.hidden = YES;
} else {
cell.progressView.hidden = NO;
}
}];
return cell;
}
VestimentaDetailCell.m
- (IBAction)likeLook:(id)sender {
if ([sender isSelected]) {
[sender setImage:[UIImage imageNamed:#"Like.png"] forState:UIControlStateNormal];
[sender setSelected:NO];
} else {
[sender setImage:[UIImage imageNamed:#"Liked.png"] forState:UIControlStateSelected];
[sender setSelected:YES];
NSLog(#"Liked Image");
}
}
Collection and table views recycle their cells. I think what's happening is that if you like cell 1 then cell 3, cell 5, so on... are all using a recycled cell 1.
To fix this, set the button color for all cells the are being dequeued into cellForRowAtItemPath to be the default button color.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// get cell and stuff
...
cell.likeButton.backgroundColor = [UIColor whiteColor];
}
Related
I am trying to preselect some of the cell according to condition, which sets those cells selected and also change its background color while drawing those cells. Now the method
didSelectItemAtIndexPath / didDeselectItemAtIndexPath
is not getting called only for those preselected cells and hence I am not able to toggle selection and background color. The select/deselect delegate methods are being called for other cells
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cellForItemAtIndexPath: %#", indexPath);
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"collectionViewCell" forIndexPath:indexPath];
if(indexPath.row != 0 && indexPath.row != 8 && indexPath.section != 0 && indexPath.section != 25){
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
NSString *blockedVal = [blockedHours valueForKey:#(indexPath.section-1).stringValue];
[cell setBlockedVal:(NSString*)blockedVal];
}
[cell addDayTimeLable:indexPath];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"didSelectItemAtIndexPath: %# ", indexPath);
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
[blockedHours setValue:#"1" forKey:#(indexPath.section-1).stringValue];
CollectionViewCell *cell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
cell.selected = YES;
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"didDeselectItemAtIndexPath %#", indexPath);
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
[blockedHours setValue:#"0" forKey:#(indexPath.section-1).stringValue];
CollectionViewCell *cell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
cell.selected = NO;
}
In CollectionViewCell.m:
Self.selected calls the setter method and hence chagnes the background color
-(void)setBlockedVal:(NSString*)blockedVal{
if([blockedVal isEqualToString:#"1"]){
self.selected = YES;
}
}
-(void)setSelected:(BOOL)selected{
NSLog(#"set selected: %d", selected);
[super setSelected:selected];
if(selected)
self.backgroundColor = [SFIColors lightGreenColor];
else
self.backgroundColor = [UIColor whiteColor];
}
Note:
(1) didHighlightItemAtIndexPath/didUnHighlightItemAtIndexPath are
getting called for preselected cells.
(2)I Just found out that setting selected via didselect/didunselect is
redundant and I just removed from my code.Noticed that setSeleted is
auto called on clicking the other cells. Still this setSelected is not
being for preselected cells
Any Inputs to fix this or another way that I can do my task would be of great help.
I found the answer in this link: UICollectionView - didDeselectItemAtIndexPath not called if cell is selected
I actually searched a lot actually, but only now I found this link.
I had let my collection view know about my selection and that did the trick:
[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
I'm adding a button to a cell of a collection view as below
- (void)activateDeletionMode:(UILongPressGestureRecognizer *)gr
{
if (gr.state == UIGestureRecognizerStateBegan)
{
NSLog(#"deletion mode");
if(self.isDeleteActive == NO){
self.isDeleteActive = YES;
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:[gr locationInView:self.collectionView]];
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
self.deletedIndexpath = indexPath.row;
self.deleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.deleteButton addTarget:self
action:#selector(deleteImage:)
forControlEvents:UIControlEventTouchUpInside];
[self.deleteButton setBackgroundImage: [UIImage imageNamed:#"delete.png"] forState:UIControlStateNormal];
self.deleteButton.frame = CGRectMake(10, 0, 10, 10);
[cell addSubview:self.deleteButton];
}
}
}
The problem is, when the cell is reused when the collection view is scrolled, I see the button displayed in this cell too. How do I avoid this happening? Code for collection view below:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:kCellID forIndexPath:indexPath];
cell.image.image = [self.imageArray objectAtIndex:indexPath.row];
return cell;
}
In the past I've done things like this, when adding the view add a tag:
self.deleteButton.frame = CGRectMake(10, 0, 10, 10);
//Mark the view with a tag so we can grab it later
self.deleteButton.tag = DELETE_BUTTON_TAG;
[cell addSubview:self.deleteButton];
Then remove it from any new recycled cells:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:kCellID forIndexPath:indexPath];
//Remove the delete view if it exists
[[cell viewWithTag:DELETE_BUTTON_TAG] removeFromSuperview];
cell.image.image = [self.imageArray objectAtIndex:indexPath.row];
return cell;
}
I tried to add checkmark image to cell but i have a problem, i just select 1 cell although i set the multiselection = YES for collectionview. And 1 more issue is when I scroll the collectionview, the selected cell is reuse, all checkmark images is incorrect. Below is the code i'm using.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TDPhotoCell *photoCell =
[collectionView dequeueReusableCellWithReuseIdentifier:TDPhotoCellIdentifier forIndexPath:indexPath];
TDPhoto *photo = self.photosArray[indexPath.row];
[photoCell.photoImage sd_setImageWithURL:photo.imageURL placeholderImage:[UIImage imageNamed:#"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL)
{
[activity removeFromSuperview];
}];
if (self.isSelectMode) {
// check the selected photo
if (self.selectedItemIndexPath != nil && [indexPath compare:self.selectedItemIndexPath] == NSOrderedSame) {
UIImageView *CheckedImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 102, 102)];
CheckedImage.tag = 118;
CheckedImage.image = [UIImage imageNamed:#"Checked_Overlay"];
[photoCell.contentView addSubview:CheckedImage];
} else {
//remove checkimage
for(UIImageView *IMGview in photoCell.contentView.subviews)
{
if (IMGview.tag == 118) {
[IMGview removeFromSuperview];
}
}
}
}
return photoCell;
}
and this is didSelectItemAtIndexPath():
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *indexPaths = [NSMutableArray arrayWithObject:indexPath];
if (self.selectedItemIndexPath)
{
// if we had a previously selected cell
if ([indexPath compare:self.selectedItemIndexPath] == NSOrderedSame)
{
// if it's the same as the one we just tapped on, then we're unselecting it
self.selectedItemIndexPath = nil;
}
else
{
// if it's different, then add that old one to our list of cells to reload, and
// save the currently selected indexPath
[indexPaths addObject:self.selectedItemIndexPath];
self.selectedItemIndexPath = indexPath;
}
}
else
{
// else, we didn't have previously selected cell, so we only need to save this indexPath for future reference
self.selectedItemIndexPath = indexPath;
}
// and now only reload only the cells that need updating
[collectionView reloadItemsAtIndexPaths:indexPaths];
}
}
Where is the incorrect in my code? Pls. give me some advice. Thanks in advance.
My problem is that the ProgressView only shows up in the first 2 cells. What is wrong with my code?
Note: My CollectionView scrolls horizontally and each cell covers the whole screen. I think this might have something to do since I tried showing all cells in the same view and they work fine. All ProgressViews show.
EDITED CODE:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
VestimentaDetailCell *cell = (VestimentaDetailCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.progressView.hidden = NO;
[cell.progressView setProgress:0.02];
PFFile *storeLooks = [self.vestimenta objectForKey:[NSString stringWithFormat:#"image_%ld", (long)indexPath.item]];
NSMutableString *precio = [NSMutableString string];
for (NSString* precios in [self.vestimenta objectForKey:[NSString stringWithFormat:#"precios_%ld", (long)indexPath.item]]) {
[precio appendFormat:#"%#\n", precios];}
[storeLooks getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error && data.length > 0) {
cell.imageFile.image = [UIImage imageWithData:data];
} else {
cell.progressView.hidden = YES;
}
} progressBlock:^(int percentDone) {
float percent = percentDone * 0.02;
[cell.progressView setProgress:percent];
if (percentDone == 100){
cell.progressView.hidden = YES;
} else {
cell.progressView.hidden = NO;
}
}];
return cell;
}
Instead of removing the progress view from the cell, you should simply setHidden:YES.
This way when the cells are reused, the progress view will be present and you can then setHidden:NO when you want to start loading stuff in that cell.
Also be careful with progress blocks inside your cells when they are reused. Remember to either cancel the loading operation if the cell is reused, or make sure the progress block only continues updating it's cell if the cell hasn't been reused for a different data item.
So for example I would set the progress to 0. where you're showing the progress view like so:
VestimentaDetailCell *cell = (VestimentaDetailCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.progressView.hidden = NO;
[cell.progressView setProgress:0.];
And because you're hiding the progressView when the data is finished loading, I would just get rid of this code in the progressBlock:
if (percentDone == 100){
cell.progressView.hidden = YES;
} else {
cell.progressView.hidden = NO;
}
I think this is related to the cells being reused as they come on screen. Since you are removing the progressView from the cell [cell.progressView removeFromSuperview] , once it is dequeued, it is still missing. You could try overriding the prepareForReuse method in your VestimentaDetailCell class to add it back so that all dequeued cells are brought back to their original state.
I am trying to select a cell in UICollectionView, it gets selected but on scroll down it selects the some other cell on the bottom and scroll up it shows some other to be selected.
Below is the code which I am using didSelectItemAtIndexPath
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *newIndex = indexPath;
cell = (CustomCollectionCell *)[collectionView cellForItemAtIndexPath:newIndex];
NSString *strId = [[masterArray valueForKey:#"id"]objectAtIndex:indexPath.row];
NSString *tempIndexRow = [NSString stringWithFormat:#"%ld",(long)indexPath.row];
NSLog(#"%#, %#,%d ,%#, %d", strId,tempIndexRow,cell.imageView.tag,[boolArray objectAtIndex:indexPath.row],indexPath.row);
if (strId && [[boolArray objectAtIndex:indexPath.row] isEqualToString:#"False"] && cell.imageView.tag == indexPath.row) {
cell.selectedImage.image = [UIImage imageNamed:#"select.png"];
[boolArray replaceObjectAtIndex:indexPath.row withObject:#"True"];
}
else{
cell.selectedImage.image = Nil;
[boolArray replaceObjectAtIndex:indexPath.row withObject:#"False"];
}
}
This is what I select for the first time
This is what I get when I scroll down
Thanks
You need to set selected item index store in to one array and at cellForRowIndex time check this array index with indexPath.row like bellow:-
selectedCellsArray alloc this array at ViewDIdLoad method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
cell = (CustomCollectionCell *)[collectionView cellForItemAtIndexPath:newIndex];
if ( [selectedCellsArray containsObject:[NSString stringWithFormat:#"%d",indexPath.row]] )
{
[selectedCellsArray removeObject:[NSString stringWithFormat:#"%d",indexPath.row]];
cell.selectedImage.image = Nil;
}
else
{
[selectedCellsArray addObject:[NSString stringWithFormat:#"%d",indexPath.row]];
cell.selectedImage.image = [UIImage imageNamed:#"select.png"];
}
}
and
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:indexPath.row];
if ( [selectedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
cell.selectedImage.image = [UIImage imageNamed:#"select.png"];
}
else
{
cell.selectedImage.image = Nil;
}
}
It is not selecting another cell. Problem is mostlikely due to you reusing cells and not reintializing them correctly when they come back on the screen. When Cells not visible, it unloaded to save memory.
So better check this condtion as well in cellForItemAtIndexPath datasource
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//You cell initialization code.
if ([[boolArray objectAtIndex:indexPath.row] isEqualToString:#"True"] ) {
cell.selectedImage.image = [UIImage imageNamed:#"select.png"];
}
else{
cell.selectedImage.image = Nil;
}
}