I have a CollectiveView with a variety of elements inside. I have managed to add a button and have attached an action to it. On pressing the button, I want that call to perform a certain activity, like change background colour for instance...
At the moment, this activity is being applied to all the cells. Any suggestions appreciated.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
cell.label.text = [NSString stringWithFormat:#"%ld",(long)indexPath.item];
cell.trackLbl.text = [NSString stringWithFormat:#"Track %ld", (long)indexPath.row + 1];
cell.trackTitle.image = [UIImage imageNamed:[[self.util trackTitleImages] objectAtIndex:indexPath.row]];
[cell.record addTarget:self
action:#selector(recording:)
forControlEvents:UIControlEventTouchUpInside];
if (isRecording) {
[cell.spinnerWidget beginCompletion:[UIColor redColor]];
}
else {
[cell.spinnerWidget beginCompletion:[UIColor colorWithRed:0.45f green:0.77f blue:0.87f alpha:1.0f]];
}
[self.view setNeedsDisplay];
return cell;
}
So as you can see, at the moment I have the 'recording' target assigned to my cell uibutton. From this, I can also get the index of the cell. My work around was to have a bool (isRecording) and reloadData. But obviously this gets applied to all cells.
If the button is in the cell use superview. For example in the case of background color
#IBAction func buttonPressed(sender: AnyObject) {
yourButton.superview.backgroundColor = UIColor(your values here)
}
Related
This is such a crazy thing happening and I just don't understand it.
I have a UICollectionView filled with images. I want to be able to select an image and draw a red border around it (or add an x, change the alpha, or do ANYTHING to it). But no matter what I do or where I put the code, ONLY the last image in the view gets the red border. Everything executes as expected after cell selection - (the correct image is removed (the one at the correct indexpath), the collectionview gets reloaded, and the document directory gets updated). Why can't I highlight the correct image?
I do have a TapGestureRecognizer to implement drawing the border if that makes a difference but it was doing the same thing when I called the code from a button press, in which case it was expected that ALL the cells would get a red border, but that didn't happen either. Only the last cell got the border???
To make it clear, say there are nine images in the collectionview. I can tap on index 0 but only index 8 ever gets the red border. Same thing if I tap on indexes 1 through even 8 itself, only index 8 ever gets the border.
Another thing I tried was to set an image of an x on every cell and hide it when presenting the collection view. I attempted to unhide it when an image was tapped at the selected index. Same thing happens. When a cell is tapped, the last cell gets the x and not the selected cell! What the heck?
All my NSLogs indicate that the correct indexpaths are being read and it is proven in the final outcome as everything gets updated but it is a poor user experience if the correct cell cannot be highlighted.
I would be most appreciative of any ideas on this ridiculousness.
The cells are filled in a normal fashion:
_cell.bookImageView.image = [_book.imageArray objectAtIndex:indexPath.row];
Here is the tap gesture in cellForItemAtIndexPath:
UITapGestureRecognizer *deleteImageTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(deleteImageTapped:)];
deleteImageTap.numberOfTapsRequired = 2;
_cell.bookImageView.tag = indexPath.row;
[_cell.bookImageView addGestureRecognizer:deleteImageTap];
_cell.bookImageView.userInteractionEnabled = YES;
This is all I'm attempting to do:
_cell.layer.borderColor = [UIColor yellowColor].CGColor;
_cell.layer.borderWidth = 2;
Or even this:
_cell.alpha = 0.4;
A few places I tried the code in:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
-(void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
//my tap gesture method
-(void)deleteImageTapped:(UITapGestureRecognizer *)gesture
//how I get the indexpath in my method
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:gesture.view.tag inSection:0];
I knows very well about your problem.
I suggest you to use a button in CellForRowAtIndexPath, instead of UiTapGesture.
First add button in your cell.
cell.yourbutton.tag = indexPath.row;
[cell.yourbutton addTarget:self action:#selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
-(void)yourButtonClicked:(UIButton*)sender
{
if (sender.tag == 0)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
}
}
It turns out that I just needed to "mention" my cell in didHighlightItemAtIndexPath and didUnhighlightItemAtIndexPath. But for whatever reason, adding the changes to the cells never got called here.
I left the UITapGestureRecognizer in my cellForItemAtIndexPath and added the changes to the cell in the method called from my tap gesture.
The basic set up is like this:
#property (nonatomic, strong) CollectionViewCell *cell;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (!_cell.selected)
{
//this "turns off" the cell changes
_cell.alpha = 1.0;
_cell.deleteButton.alpha = 0.0;
_cell.bookImageView.layer.borderColor = nil;
_cell.bookImageView.layer.borderWidth = 0;
}
UITapGestureRecognizer *deleteImageTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(deleteImageTapped:)];
deleteImageTap.numberOfTapsRequired = 2;
_cell.bookImageView.tag = indexPath.row;
[_cell.bookImageView addGestureRecognizer:deleteImageTap];
_cell.bookImageView.userInteractionEnabled = YES;
return _cell;
}
-(void)deleteImageTapped:(UITapGestureRecognizer *)gesture
{
_cell.alpha = 0.4;
_cell.deleteButton.alpha = 1.0;
_cell.bookImageView.layer.borderColor = [UIColor redColor].CGColor;
_cell.bookImageView.layer.borderWidth = 5;
//more code to delete image blah blah blah
}
And then this is where I "mention" my cell
- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
}
- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
_cell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
}
Hope this can help someone!
I need cells in a UICollectionView which will either show text or an image.
I am implementing this by creating my own UICollectionViewCell having two properties - UIImage property and NSString property.
Depending on which property the caller sets, I show either the image or the text. The calling collectionview is also reusing the cells by using :
cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
But whenever I reload the collectionview in my code the cells get all messed up - they show empty buttons. What am I doing wrong? Let me know if you need me to post any other specific code piece. I didn't want to clutter my question with useless code.
In my CollectionViewCell, I am resetting both the label and the image:
-(void) prepareForReuse {
[super prepareForReuse];
[_button setTitle:#"" forState:UIControlStateNormal];
[_button setImage:nil forState:UIControlStateNormal];
[_button removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
}
cellForItemAt code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
NSInteger position = indexPath.row;
long listIdx = position - 1;
if (listIdx == ADD_ICON_INDEX) {
UIImage * addImage = [self _getAddImage];
[cell setButtonImage:addImage target:self action:#selector(_addButtonClicked:)];
cell.button.tag = listIdx;
return cell;
}
NSString *title= _visibleButtons[listIdx];
[cell setButtonTitle:title target:self action:#selector(_buttonClicked:)];
cell.backgroundColor = [UIColor clearColor];
cell.button.tag = listIdx;
return cell;
}
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
In the UICollectionView, I've got a custom UICollectionViewCellClass, where prepareForReuse is overridden for default formatting staff.
I've got an NSMutableArray containing NSIndexPaths from didSelectItemAtIndexPath:.
In cellForItemAtIndexPath: I reformat the selected cells so they appear selected.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
ButtonCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"ButtonCell" forIndexPath:indexPath];
NSString *title = self.ingredientsBook.names[indexPath.item];
cell.label.text = title;
if ([self isSelectedIndexPath:indexPath]){
cell.backgroundColor = [UIColor whiteColor];
cell.label.textColor = [UIColor blueColor];
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
self.searchButton.enabled = YES;
ButtonCell *cell = (ButtonCell *)[collectionView cellForItemAtIndexPath:indexPath];
[selectedCellIndexPaths addObject:indexPath];
NSLog(#"%#", selectedCellIndexPaths);
cell.backgroundColor = [UIColor whiteColor];
cell.label.textColor = [UIColor blueColor];
NSString *name = self.ingredientsBook.names[indexPath.item];
[self.selectedIngredientNames addObject:name];
}
The problem is that when I tap the first cell it's not possible to select the 16th or 17th.
Or if I tap the first three ones it's not possible to select the three last ones.
The didSelectItemAtIndexPath is not being called I suppose.
I feel that it has to be something really simple but I can't see it right now.
I tried to put NSLogsin shouldSelectItemAtIndexPath for understand if that method was called and the method is not being called at all. This happens when there's a distance of 16 cells between the selected one and the problematic one.
Here are other data source methods and isSelectedIndexPath:
-(BOOL)isSelectedIndexPath:(NSIndexPath *)indexPath{
for (NSIndexPath *test in selectedCellIndexPaths){
if (test == indexPath){
return YES;
}
}
return NO;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [self.ingredientsBook.names count];
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"%#", indexPath);
return YES;
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
self.searchButton.enabled = ([[collectionView indexPathsForSelectedItems] count] > 0);
ButtonCell *cell = (ButtonCell *)[collectionView cellForItemAtIndexPath:indexPath];
cell.backgroundColor = [UIColor blackColor];
cell.label.textColor = [UIColor whiteColor];
[selectedCellIndexPaths removeObject:indexPath];
NSString *name = self.ingredientsBook.names[indexPath.item];
[self.selectedIngredientNames removeObject:name];
}
I found two problems. The prepareForReuse method seemed to be screwing things up, so I just deleted it. The main problem though, was the way you were implementing isSelectedIndexPath:. As soon as it finds the first selected item as you loop through the items, it returns YES and exits the loop. What you want to do, is just check if the indexPath is contained in the selectedCellIndexPaths array:
-(BOOL)isSelectedIndexPath:(NSIndexPath *)indexPath{
if ([selectedCellIndexPaths containsObject:indexPath]) {
return YES;
}else{
return NO;
}
}
Or, if you prefer to use a more succinct syntax, you can replace the if-else block with:
return ([selectedCellIndexPaths containsObject:indexPath])? YES : NO;
I recently faced the exact same issue with my app. UICollectionViewCell selection worked properly prior to iOS 8.3, subsequently I started to see some strange behaviour. Cells that were not actually selected would appear selected, other cells, seemingly at random could not be selected.
I had both custom setSelected and prepareForResuse methods implemented on a UICollectionViewCell subclass as such:
-(void)setSelected:(BOOL)selected
{
[super setSelected:selected];
if (selected)
{
[[self selectedIndicator] setHidden:NO];
}
else
{
[[self selectedIndicator] setHidden:YES];
}
}
-(void)prepareForReuse
{
[[self imageView] setImage:nil];
}
The prepareForReuse method simply reset an image view in the custom cell.
In my prepareForReuse method I did not make a call to [super prepareForReuse] (which according to the documentation does nothing by default). When I added the call to [super prepareForReuse] all selection worked as intended. Although Apple states the default implementation does nothing, they also recommend that super should be called. Following this recommendation solved my issue.
In iOS 10 I found that programmatically clearing a UICollectionView that had multiple-selection enabled was buggy when prefetching was enabled. And of course prefetch is on by default in iOS 10.
I have a UICollectionView, which is populated with cells, and each cell has a gradient button (MKgradientbutton https://github.com/mikekatz/iOS-UI-Utils/tree/master/Classes/Button%20Widgets). The view is 'changed' by pressing one of 5 other buttons, which in turn loads the data for the buttons into an array which provides the datasource for the uicollectionview. One of the paramaters is the gradient button colour. However on reloading the datasource and performing a [self.collectionView reloadData] the buttons title and the other paramaters will change but the colour will quite often change to completely the wrong one. Repeatedly reloading the data will solve this problem - however, even more strangely, one press of the button seems to correct the colour of the button! I have NSLogged the datasource array on the line before the reloadData takes place and all of the colours are correct. I have pasted some code below - please bear with me as the app is still in the very early stages of development!
I really can't see any issues with the code - and I believe that the UICollectionView needs 'redrawn' but I assumed [self.CollectionView reloadData] would solve that!
Pulling data from sqlite db into and object then adding to array:
Bird *bird = [[Bird alloc] init];
bird.birdName = [results stringForColumn:#"name"];
bird.buttonColour = [results stringForColumn:#"btncolour"];
[birds addObject:bird];
The array 'birds' being used to populate UICollectionView:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView2 cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = [collectionView2 dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
Bird *bird = [birds objectAtIndex:(indexPath.row)];
//Check button colour against index path of item
NSLog(#"****INDEX PATH %i",indexPath.section*2 + indexPath.row);
NSLog(#"****colour %#",bird.buttonColour);
[cell.button setTitle:bird.birdName forState:UIControlStateNormal];
[cell.button setTag:[birds indexOfObject:bird]];
if ([bird.buttonColour isEqualToString:#"Green"]) {
[cell.button setButtonColor:[UIColor greenColor]];
}
if ([bird.buttonColour isEqualToString:#"Orange"]) {
[cell.button setButtonColor:[UIColor orangeColor]];
}
if ([bird.buttonColour isEqualToString:#"Red"]) {
[cell.button setButtonColor:[UIColor redColor]];
}
if ([bird.buttonColour isEqualToString:#"Blue"]) {
[cell.button setButtonColor:[UIColor blueColor]];
}
cell.button.titleLabel.numberOfLines = 0;
cell.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.button.titleLabel.textAlignment = NSTextAlignmentCenter;
return cell; }
collectionViewCell.h (the gradient button is the only item in the cell):
#import <UIKit/UIKit.h>
#import "MKGradientButton.h"
#interface CollectionViewCell : UICollectionViewCell
#property (nonatomic) IBOutlet MKGradientButton* button;
#end
You may have to tell the button to redraw itself via [cell.button setNeedsDisplay].