I am using collectionView in my App. I am setting image for the cell backgroundView in didSelect delegate. But When i select one cell indexPath the image is getting set for 3 cell indexPath. When i scroll the collectionView the images are getting changed randomly? Please Help me. thanks in advance.
- (void)viewDidLoad
{
[super viewDidLoad];
[collection registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:uio];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 50;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"index %#",indexPath);
UICollectionViewCell *cell = [collection cellForItemAtIndexPath:indexPath];
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
}
That's because you reuse your cell. An option would be to have an dictionary variable to say that your cell has been selected and reset the image if it has not been.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"index %#",indexPath);
UICollectionViewCell *cell = [collection cellForItemAtIndexPath:indexPath];
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
[selectedDictionary setObject:[NSNumber numberWithBool:YES] forKey:[NSNumber numberWithInteger:indexPath.row]];
}
Then in your cellForItemAtIndexPath method you would check that value
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
BOOL selected = [[selectedDictionary objectForKey:[NSNumber numberWithInteger:indexPath.row]] boolValue];
if(selected){
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
}else{
cell.backgroundView = nil;
}
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
Of course if you use some kind of object as model, it would appropriate to have a selected variable in here, you won't need a nsdictionary any more.
The Problem is dequeueReusableCellWithReuseIdentifier.
When you scroll UICollectionview then cell are reused that is problem
add Collectionview inside scrollview.
Try this Inside:
Scroll_View is Your Scroll View
collection is Your Collectionview
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
self.Scroll_View.contentSize = CGSizeMake(self.view.frame.size.width, collectionView.contentSize.height);
CGRect fram_For_Collection_View = self.collection_view.frame;
fram_For_Collection_View.size.height = collectionView.contentSize.height;
self.collection.view.frame = fram_For_Collection_View;
}
Your -collectionView:didSelectItemAtPath: is adding a new image view to the cell. Nothing is removing that image view when the cell is reused. So, when you say:
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
in your -collectionView:cellForItemAtIndexPath:, you're may get back some cell that already has one or more image views.
My suggestion would be to add the image view to the cell in the cell prototype, perhaps in your storyboard or in the cell's initializer. Have your -collectionView:cellForItemAtIndexPath: set the image for that image view to the correct image for the given path.
What's happening is that UICollectionView reuses cells. So in didSelectItemAtIndexPath: you set the cell background, but then the UICollectionView reuses that same cell as needed (and you're not resetting the cell.backgroundView in cellForItemAtIndexPath:).
The way to fix this is to maintain an NSIndexSet of selected cells. In didSelectItemAtIndexPath: you can add the index of the item that was selected, and then force a reload of that item by calling reloadItemsAtIndexPaths. Then, in your cellForItemAtIndexPath: check the index set to see if the selected index is included, and if so, set the backgroundView of the cell.
I had the same issue few days ago & I posted a question here. Here is the answer I got & it works for me.
Collection View Cell multiple item select Error
And also if you are using a custom cell you can add this code to the init method of that cell & it will work too.
CGFloat borderWidth = 6.0f;
UIView *bgView = [[UIView alloc] initWithFrame:frame];
bgView.layer.borderColor = [UIColor redColor].CGColor;
bgView.layer.borderWidth = borderWidth;
self.selectedBackgroundView = bgView;
Related
Well this so weird . I also hesitate to post this question but I haven't got any solution that's why I decided to post it. Here I go.
I have a XIB i have subclassed it in UICollectionView. This is my code in didSelectMethod:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
cell.nameLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:0.8];
[cell setSelected:NO];
cell.nameLabel.hidden = NO;
cell.nameLabel.text = #"HHHHHHH";
cell.nameLabel.backgroundColor = [UIColor redColor];
}
Now when I run my code I get correct Output. But when I select any cell then text of my UILabel is hidden I can only see the background colour.
When I select another one then I get back the text of previously selected UILabel.
I haven't written any other code I not able to figure it OUT.
When I select any cell:
When I select another cell:
When I select Previous Cell:
Here is my code for making cells:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Get reusable cell reference
CollectionViewCell *cell = (CollectionViewCell *)[cv dequeueReusableCellWithReuseIdentifier:#"CollectionViewCell" forIndexPath:indexPath];
[cell updateText];
return cell;
}
This method is in custom Class
- (void) updateText{
self.nameLabel.text = name;
}
Go to your nib/xib file and check the highlighted color weather it is clear or some other color. If it is clear than change as per your requirement.
Capture your selected index in didSelectItemAtIndexPath and reload you collection view. In cellForIndexPath check for your selected index and do the stuff there. Do some thing like below
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if (selectedIndex == indexPath.row) {
cell.nameLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:0.8];
[cell setSelected:NO];
cell.nameLabel.hidden = NO;
cell.nameLabel.text = #"HHHHHHH";
cell.nameLabel.backgroundColor = [UIColor redColor];
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedIndex = indexPath.row;
[collectionView reloadData];
}
It's high probably because your CollectionViewCell is reusable cell. You hidden state is reused for other cells too. You should set it in your cellForIndexPath method.
- (void)updateText
{
self.nameLabel.text = name;
cell.nameLabel.hidden = YES; // set here it's default value
}
Create a Bool Array for each cell and then update the values when any cell is selected i.e in didSelectItemAtIndexPath.
Also inside cellForItemAtIndexPath, you can go as quoted by Vijay except just taking in consideration only one selected cell you can now handle multiple selected cells.
The real issue is that you should have set highlighted color of label to clear color in xib unknowingly. Check your nib and change highlighted property to desired color you want. When a cell is selected the subviews changes their color to their highlighted color set in xib. Hope this will help you. enter image description here
I have a very frustrating problem with UICollectionView and its dequeuing mechanism.
In a nutshell, I have a custom UIView with a label inside. I set this up as the selection background view in my custom cell as follows:
//My Custom Cell Class
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
_selectionView = (MyCustomView *)[[[NSBundle mainBundle] loadNibNamed:#"MyNibName" owner:self options:nil] objectAtIndex:0];
self.selectedBackgroundView = _selectionView;
[self bringSubviewToFront:_selectionView];
}
return self;
}
Note that all the layout work, etc for both the cell and selectedBackgroundView is done in the nibs.
Whenever the cell is selected, I'd like to set custom text in the label for selectionView so in my custom cell I also have the following method:
//In my Custom cell class
- (void) setSelectedViewLabelText:(NSString *)paramText{
if(!self.isSelected){
return;
}
self.selectionView.label.text = paramText;
}
To set the text I have in my UICollectionViewController:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCellClass *selectedCell = (MyCellClass *)[self.collectionView cellForItemAtIndexPath:indexPath];
[selectedCell setSelectedViewLabelText:someString];
}
The problem is that whenever the UICollectionView recycles the cell, it inits them again and obviously the setSelectedViewLabelText method isn't called.
I have an annoying feeling that I may have to track the selected indexPaths and enumerate through them to see if a cell is selected and call the method, but I have potentially large data sets and can foresee how this will become a performance problem.... any ideas?
Thanks in advance!
Keep track of your selected cell, like:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCellClass *selectedCell = (MyCellClass *)[self.collectionView cellForItemAtIndexPath:indexPath];
[selectedCell setSelectedViewLabelText:someString];
selectedIndexPath = indexPath;
}
In cellForItemAtIndexPath check indexPath:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
//.......
if([selectedIndexPath isEqual:indexPath]){
[cell setSelectedViewLabelText:someString];
}
return cell;
}
Hope this helps.. :)
I can change my collection view cell background color in cellforitematindexpath but not in didselectitematindexpath using the code below (i only change the color in one place however). How does this happen? I know that didselectitematindexpath is being called.
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CollectionView" forIndexPath:indexPath];
cell.layer.borderWidth=1.0f;
cell.layer.borderColor=[UIColor grayColor].CGColor;
cell.label.text = #"test"
cell.contentView.backgroundColor = [UIColor redColor];
return cell;
}
-(void)collectionView:(UICollectionView *)cv didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"selected cell");
CollectionViewCell *cell = [self collectionView:cv cellForItemAtIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor redColor];
}
Several things wrong here.
You're using the wrong method to get the selected cell. Never call the delegate method directly, instead ask the collection view for the cell directly like this:
CollectionViewCell *cell = [cv cellForItemAtIndexPath:indexPath];
You're modifying the cell in a place where those modifications will not persist. When you scroll and that cell gets reused, the color will get wiped out. Instead you should call reloadItemsAtIndexPaths: when the cell gets selected, and have some code in collectionView:cellForRowAtIndexPath that checks the selected property of the cell and sets the color you want based on that. That way, the layout will remain consistent when you scroll and cells are reused.
use this..
self.collectionView.allowsMultipleSelection = YES;
then..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath**
{
static NSString *identifier = #"Cell";
RecipeViewCell *cell = (RecipeViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section] objectAtIndex:indexPath.row]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame-2.png"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame.png"]];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if (shareEnabled) {
NSString *selectedRecipe = [recipeImages[indexPath.section] objectAtIndex:indexPath.row];
[selectedRecipes addObject:selectedRecipe];
}
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
if (shareEnabled) {
NSString *deSelectedRecipe = [recipeImages[indexPath.section] objectAtIndex:indexPath.row];
[selectedRecipes removeObject:deSelectedRecipe];
}
}
I am trying to make some photoPicker with CollectionView.
Have
allowsMultipleSelection = YES
Using following method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedPictures = [NSMutableArray array];
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]];
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
While I am selecting cells, it's always adding to MutableArray only one object according it's indexPath. What could be an issue?
Why don't u keep the selectedPictures as a member variable
in your code
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedPictures = [NSMutableArray array]; //keep on creation the new array on each selection
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]]; //adding the selected images means single image
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
try this
put his in viewDidLoad
- (void)viewDidLoad
{
selectedPictures = [[NSMutableArray alloc]init]; //initilise hear
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// selectedPictures = [NSMutableArray array]; //keep on creation the new array on each selection
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]]; //adding the selected images means single image to already initialised array
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
Hope this helps u .. :)
it may be caused by not calling super. While the documentation for UICollectionReusableView fails to mention this, the documentation for UITableViewCell, which has the same method, does.
- (void)prepareForReuse
{
[super prepareForReuse]
// Your code here.
}
Old Answer:
This may be a bug with the UICollectionView.
What's happening is cells that were previously selected are being reused and maintain the selected state. The collection view isn't setting selected to "NO".
The solution is to reset the the selected state in prepareForReuse of the cell:
- (void)prepareForReuse
{
self.selected = NO;
}
If the reused cell is selected, the collection view will set selected to "YES" after prepareForReuse is called.
This is something the UICollectionView should be doing on it's own. Thankfully the solution is simple. Unfortunately I spent a ton of time working around this bug by tracking my own select state. I didn't realize why it was happening until I was working on another project with smaller cells.
Also Try this
I'm not seeing why this would take place. I do not believe the issue is the use of row vs item, though you really should use item. I can imagine, though, if your collection view has more than one section, that only looking at row/item but ignoring section would be a problem (i.e. it would select the same item number in every section).
To cut the Gordian knot, I'd suggest saving the NSIndexPath of the selected item, and then using that for the basis of comparison. That also makes it easy to render an optimization in didSelectItemAtIndexPath. Anyway, first define your property:
#property (nonatomic, strong) NSIndexPath *selectedItemIndexPath;
And then implement cellForItemAtIndexPath and didSelectItemAtIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.imageView.image = ...
if (self.selectedItemIndexPath != nil && [indexPath compare:self.selectedItemIndexPath] == NSOrderedSame) {
cell.imageView.layer.borderColor = [[UIColor redColor] CGColor];
cell.imageView.layer.borderWidth = 4.0;
} else {
cell.imageView.layer.borderColor = nil;
cell.imageView.layer.borderWidth = 0.0;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// always reload the selected cell, so we will add the border to that cell
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];
}
Check also this
Your observation is correct. This behavior is happening due to the reuse of cells. But you dont have to do any thing with the prepareForReuse. Instead do your check in cellForItem and set the properties accordingly. Some thing like..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cvCell" forIndexPath:indexPath];
if (cell.selected) {
cell.backgroundColor = [UIColor blueColor]; // highlight selection
}
else
{
cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}
I solved my issue;
The problem was very simple, I should have initialise MutableArray not in the Method didSelectItemAtIndexPath, but in the ViewDidLoad. Now it adding pictures one by one
I am configuring a UICollectionViewCell in a subclass, it adds 2 subviews to the contentView property, both are UIImageView and both have the hidden property set to YES. These subviews are "checked" and "unchecked" images that overlay the primary UIImageView in the cell to indicate whether or not the current cell is selected using UICollectionView's "multiple select" feature.
When the cell is tapped, collectionView:didSelectItemAtIndexPath: is called on the delegate, and I'd like to setHidden:NO on the "checked" UIImageView. Calling this on the cell does nothing at all -- the cell is seemingly locked in its originally drawn state.
Is it possible to make changes to a cell outside collectionView:cellForItemAtIndexPath:? I have tried manually adding subviews within collectionView:didSelectItemAtIndexPath:, but it just makes absolutely no change to the UI. I have verified that the delegate method is getting called, it's just not making my cell changes.
- (void) collectionView(UICollectionView *)cv didSelectItemAtIndexPath(NSIndexPath *)indexPath {
ShotCell *cell = [self collectionView:cv cellForItemAtIndexPath:indexPath];
UILabel *testLabel = UILabel.alloc.init;
testLabel.text = #"FooBar";
testLabel.sizeToFit;
[cell.contentView.addSubview testLabel];
}
The way you're trying to do this is incorrect. You need to keep a reference to the selected cell or cells in a property. In this example, I use an array to hold index paths of the selected cells, then check whether the index path passed in to cellForItemAtIndexPath is contained in that array. I unselect the cell if you click on one that's already selected:
#interface ViewController ()
#property (strong,nonatomic) NSArray *theData;
#property (strong,nonatomic) NSMutableArray *paths;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.paths = [NSMutableArray new];
self.theData = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight"];
[self.collectionView registerNib:[UINib nibWithNibName:#"CVCell" bundle:nil] forCellWithReuseIdentifier:#"cvCell"];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:flowLayout];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.theData.count;
}
-(CVCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cvCell";
CVCell *cell = (CVCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
cell.label.text = self.theData[indexPath.row];
if ([self.paths containsObject:indexPath]) {
[cell.iv setHidden:NO]; // iv is an IBOutlet to an image view in the custom cell
}else{
[cell.iv setHidden:YES];
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if ([self.paths containsObject:indexPath]) {
[self.paths removeObject:indexPath];
}else{
[self.paths addObject:indexPath];
}
[self.collectionView reloadItemsAtIndexPaths:#[indexPath]];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(150, 150);
}