UICollectionViewCell: Add a view in cell on tap - objective-c

I have a uicollectionview and I am trying to add a a view to the collectionview cell when it is tapped.
Here is the code I have tried to implement
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
WeekCell *cell = (WeekCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"%#", cell.descLabel.text);
UIView *view = [[UIView alloc]initWithFrame:cell.backgroundView.bounds];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 40, 40)];
label.text = #"new label";
[view addSubview:labels];
view.backgroundColor = [UIColor whiteColor];
[cell.backgroundView addSubview:view];
}
Here WeekCell is a custom UICollectionViewCell with a property view , backgroundview. You will also notice an NSLog in the code. This verifies that the correct cell is being retrieved. What is not working is according to the code, the text should change to white with a new UILabel but this is not the case. The cell's appearance does not change.
EDIT
As suggested I have tried to directly modify the model and call the reloadItemsAtIndexPaths to reload the data. I get an issue where the the "tapped behaviour" is being copied on to untapped cells.
Here is the new code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if([[modelArray objectAtIndex:indexPath.row] isEqualToString:#"1"]){
cell.overviewView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageString]];
}else{
cell.overviewView.alpha = 0;
}
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
WeekCell *cell = (WeekCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"%#", cell.descLabel.text);
if([[modelArray objectAtIndex:indexPath.row] isEqualToString:#"1"]){
[modelArray setObject:#"0" atIndexedSubscript:indexPath.row];
}else{
[modelArray setObject:#"1" atIndexedSubscript:indexPath.row];
}
NSLog(#"sequence : %#", modelArray);
[collectionView reloadItemsAtIndexPaths:#[indexPath]];
}
What I am doing is changing the alpha value of the view in the tapped cell to 0. This causes the other cells at random order to disappear once I scroll.

Instead of:
[cell.backgroundView addSubview:view];
... add to content view:
[cell.contentView addSubview:view];
Remarks:
Direct manipulation on collection view cell can cause unexpected results. For instance if cell gets reused. Better to update the model that gets rendered by the cell and call reloadItemsAtIndexPaths: that will automatically(internally) call collectionView:cellForItemAtIndexPath: which should configure(or invoke configuration routine) for the cell that can adjust its presentation.
UPDATE:
You should reset alpha of cells that should be visible, bellow is collectionView:cellForItemAtIndexPath: method with correction:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if([[modelArray objectAtIndex:indexPath.row] isEqualToString:#"1"]){
cell.overviewView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageString]];
cell.overviewView.alpha = 1;
} else {
cell.overviewView.alpha = 0;
}
}

The backgroundView property of a UICollectionViewCell is placed behind the content view. So the reason for the label not being visible could be because it is masked by the content view.
You could set clearColor for the Content view views or add the new view to the contentView;
[cell.contentView addSubview:view];
Hope this information is helpful.

Related

Get correct button from custom UICollectionViewCell

I have a custom UICollectionView with custom UICollectionViewCell, I have a like button on my cell (that has been connected to the CustomCVCell.h file) and I need to change the background of this button when it gets pressed. What I did was declaring the action for the button on the cellForItemAtIndexPath: method like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
FolderProducts *item = _feedItems[indexPath.item];
[cell.like addTarget:self action:#selector(likeProduct:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
Then on the button action I tried to change the background like this:
- (void)likeProduct:(UIButton *)button {
[button setImage:[UIImage imageNamed:#"dislike.png"] forState:UIControlStateNormal];
}
It works but then other random cell's like button has their image changed and I can't understand why..
I also tried to retrieve the correct cell by using:
CollectionViewCell *cell = (CollectionViewCell *)button.superview.superview;
And then:
[button setImage:[UIImage imageNamed:#"dislike.png"] forState:UIControlStateNormal];
But the result is still wrong
the problem is due to reusing of the collection view cell because of this u are getting the random button having the same image
the solution for this would be maintaining the array and storing the selected index paths of the liked button
for example u can do like below
Define a custom deleagte in CustomCVCell.h
#import <UIKit/UIKit.h>
#class CustomCVCell; //forword decleration
#protocol CustomCellDelegate <NSObject>
- (void)customCell:(CustomCVCell *)cell actionForButton:(UIButton *)inButton; //hear u are passing the cell and the button
#end
#interface CustomCVCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UIButton *like ; //a button with outlet
#property (weak, nonatomic) id<CustomCellDelegate> cellDelegate;// define a custom delegate
- (IBAction)likeProduct:(UIButton *)sender; // set action to cell not in the controller
//... other code
#end
and in CustomCVCell.m
#import "CustomCVCell.h"
#implementation CustomCVCell
- (id)initWithFrame:(CGRect)frame
{
self = [CustomCVCell cell];
if (self)
{
}
return self;
}
- (void)awakeFromNib {
// Initialization code
}
//handle button action in the cell
- (IBAction)likeProduct:(UIButton *)sender
{
//this action you want it to be in controller, call a delegate method
if([self.cellDelegate respondsToSelector:#selector(customCell:actionForButton:)])
{
[self.cellDelegate customCell:self actionForButton:sender]; //implent the action in the controller
}
}
#end
and in controller
- (void)viewDidLoad
{
[super viewDidLoad];
UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc]init];
//..set up code
[_aCollectionView registerClass:[CustomCVCell class] forCellWithReuseIdentifier:#"CELL"];
likedCells = [[NSMutableArray alloc]init]; //to hold the index paths of the liked cells
}
//..other code
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCVCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CELL" forIndexPath:indexPath];
if([likedCells containsObject:indexPath]) //based on the indexaths paths u can set the images of the liked cell and if not set nil
{
[cell.like setBackgroundImage:[UIImage imageNamed:#"22.jpg"] forState:UIControlStateNormal];
}
else
{
[cell.like setBackgroundImage:nil forState:UIControlStateNormal];
}
cell.backgroundColor = [UIColor greenColor]; //for testing
cell.cellDelegate = self; //this must be important
return cell;
}
//as the like button cliks this method will trigger since u are passing the cell and the button, u can get the index path of the cell
- (void)customCell:(CustomCVCell *)cell actionForButton:(UIButton *)inButton
{
//hear u will get both cell and its button
//[inButton setImage:[UIImage imageNamed:#"22.jpg"] forState:UIControlStateNormal];
[inButton setBackgroundImage:[UIImage imageNamed:#"22.jpg"] forState:UIControlStateNormal]; //set the image
[likedCells addObject: [_aCollectionView indexPathForCell:cell]]; //and store the index path in the likedCells array
}
that's it if u want reverse the action same remove the index path
The basic idea is that you need to convert the buttons coordinate space to the collection views coordinate space and then retrieve the indexPath.
If you need to use Objective-C, lets create a function that returns the indexPath of a given cell's subview:
- (NSIndexPath *)indexPathForCellContainingView:(UIView *)view inCollectionView:(UICollectionView *)collectionView {
CGPoint viewCenterRelativeToCollectionView = [collectionView convertPoint:CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)) fromView:view];
NSIndexPath *cellIndexPath = [collectionView indexPathForItemAtPoint:viewCenterRelativeToCollectionView];
return cellIndexPath
}
Now in your button handler:
- (void)likeProduct:(UIButton *)button {
[button setImage:[UIImage imageNamed:#"dislike.png"] forState:UIControlStateNormal];
NSIndexPath *buttonIndexPath = [self indexPathForCellContainingView:button];
UICollectionViewCell *tappedCell = [collectionView cellForItemAtIndexPath:buttonIndexPath];
}
Remember, your cell will be reused when it scrolls out of bounds so you definitely need to keep track of this state. Either you persist it to disk or you have a simple NSDictionary that manages the state of each button.
I have a convenient Gist in Swift that solves exactly that for UICollectionView and UITableView as well.
There are similar questions here and here
Assign a tag to your button to identify it.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
FolderProducts *item = _feedItems[indexPath.item];
[cell.like addTarget:self action:#selector(likeProduct:) forControlEvents:UIControlEventTouchUpInside];
cell.like.tag = indexPath.item;
return cell;
}
Then in the implementation method add below code,
- (void)likeProduct:(UIButton *)button {
UIButton *btn = (UIButton *)sender;
[button setImage:[UIImage imageNamed:#"dislike.png"] forState:UIControlStateNormal];
}
Note : The best way to achieve multiple/single selection is via id associated with your likes. Store that id using collection view then after reload the table from the button method.
Let me know if you still need more help..
You can set tag of button & retrieve tag in your selector.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
FolderProducts *item = _feedItems[indexPath.item];
cell.like.tag = indexPath.item
[cell.like addTarget:self action:#selector(likeProduct:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
because your not setting tag for your buttons in cell. So these all are selected at once. You can set their tag like indexPath.item and catch their tag in didSelectItemAtIndexPath and select the desired item.

Get position of imageView on cell of a UICollectionView

I have a UICollectionView with custom cell that has some labels and an Image, I need to get where the image is situated in order to place a mask on top of it, I tried with the following code but every mask gets applied on top of the first cell.
This is the code I used:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
UIView *darken = [[UIView alloc]init];
darken.frame = cell.img.frame;
darken.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
[collectionView insertSubview:darken aboveSubview:cell.img];
return cell;
}
How can I get the frame position of every single image on the cells?
Change your code to create just one view.
#define IMAGE_TAG 9999
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (![cell viewWithTag:IMAGE_TAG])
{
UIView *darken = [[UIView alloc]init];
darken.frame = cell.img.frame;
[darken setTag:IMAGE_TAG];
darken.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
[cell insertSubview:darken aboveSubview:cell.img];
}
return cell;
}

UIImageView in UICollectionViewCell in UITableViewCell bug

The settings for the UICollectionView were defined using IB (ie scroll direction: horizontal, etc), and was embedded in UITableViewCell using IB.
UICollectionViewCell displays, images display, however, images are stacked on top of one another, instead of one image per one cell with fidelity.
I made individual UIImageView for each picture as instance variables, and same occurred using if and switch statements in the cellForItemAtIndexPath message.
Since IB was used, it may be a stretch to identify the bug, however, would you please help to identify the bug in case it is obvious from the code? Thanks.
#implementation AccountTableViewCell
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
imageArray = #[[UIImage imageNamed:#"image1.png"], [UIImage imageNamed:#"image2.png"], [UIImage imageNamed:#"image3.png"], [UIImage imageNamed:#"image4.png"], [UIImage imageNamed:#"image5.png"]];
self.oCollectionView.dataSource = self;
[self.oCollectionView setFrame:self.contentView.frame];
[self.contentView addSubview:self.oCollectionView];
self.oCollectionView.backgroundColor = [UIColor clearColor];
[self.oCollectionView reloadData];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return imageArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"accountCell" forIndexPath:indexPath];
UIImageView* iv = [[UIImageView alloc] init];
[cell.contentView addSubview:iv];
[iv setFrame:cell.contentView.frame];
iv.image = imageArray[indexPath.row];
return cell;
}
#end
It's because you keep on adding an UIImageView to the cell each time it's dequeued.
Instead, you should subclass the UICollectionViewCell (let's call it "MYCollectionViewCell", add a UIImageView to the cell subclass in the storyboard and set the UIImageView as an outlet on the subclass.
Then, within cellForItemAtIndexPath, set that imageView's image like so:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"accountCell" forIndexPath:indexPath];
cell.imageView.image = imageArray[indexPath.row];
return cell;
}

UIView addSubview nor working in UITableViewCell

barView I have a Table View and, in each cell, I want to draw some statistic bars. I have an empty UIView created in my custom cell, its name is mainView and it is the view where the chart is drawn.
In cellForRowAtIndexPath I'm doing this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
CGRect barRect = CGRectMake(0, 0, 100, 10);
UIView *barView = [[UIView alloc] initWithFrame:barRect];
[barView setBackgroundColor:[UIColor redColor]];
[cell.mainView addSubview:barView];
return cell;
}
The barView is not displaying on loading. It only shows after I change and return from another tab or after the cell goes out of the screen.
Notes:
I have tried [cell.mainView setNeedsDisplay];
I have a header made with another custom cell.
Try this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
CGRect barRect = CGRectMake(0, 0, 100, 10);
UIView *barView = [[UIView alloc] initWithFrame:barRect];
[barView setBackgroundColor:[UIColor redColor]];
[cell addSubview:barView]; // use cell.mainView if you have that created somewhere other than here (like IB)
}
return cell;
}
dequeueResuable... can return a nil cell. So you need to have a check, to see if you should alloc/init a new cell. In the code I gave you if dequeueResuable... returns you a proper cell then it would have been created with the subview intact already. So no need to re-add it (hence creating it only in if (!cell))
Your description clearly indicates you are filling your barView outside this function. So that it becomes visible only after transition.
Or you are simply overwriting it (or even overwriting mainview) outside what you have provided here.
Check your viewdidload and viewdidappear and the likes.
I have solved it easily with:
- (void)viewDidAppear:(BOOL)animated
{
[self.tableView reloadData];
}
It seems that adding a subview cant be done before the view is created in the window, before the cells are completely loaded.

Set background color of UITableViewCell

I have looked around to find a solution for setting the background color of the accessoryView to the same background color as the cellĀ“s contentView.
cell.contentView.backgroundColor = [UIColor colorWithRed:178/255.f green:14/255.f blue:12/255.f alpha:0.05];
cell.accessoryView.backgroundColor =[UIColor colorWithRed:178/255.f green:14/255.f blue:12/255.f alpha:0.05];
There is a solution that works but only let me use one color for all cells.
cell.contentView.superView.backgroundColor = [UIColor redColor];
Is the only solution to not use the accessoryView and use an image instead?
Thanks!
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor greenColor];
}
Using this UITableViewDelegate method, you can set the color of cells to different colors. Note that Apple explicitly advise you to make changes to the backgroundColor property within the tableView:willDisplayCell:ForRowAtIndexPath: method in the docs, which state:
If you want to change the background color of a cell, do so in the tableView:willDisplayCell:forRowAtIndexPath: method of your table view delegate
Indeed, in iOS 6, changes to the property from anywhere else (like the tableView:cellForRowAtIndexPath: method) would have no effect at all. That no longer seems to be the case in iOS 7, but Apple's advice to modify the property from within tableView:willDisplayCell:ForRowAtIndexPath: remains (without any explanation).
For alternating colors, do something like this example:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row % 2) {
cell.backgroundColor = [UIColor yellowColor];
} else {
cell.backgroundColor = [UIColor redColor];
}
}
I struggled with this one for a little while too and resorted to creating a custom image with the accessory. But I just found this solution that works well and doesn't require a custom image. The trick is to change the cell's backgroundView color not the backgroundColor.
UIView *myView = [[UIView alloc] init];
if (indexPath.row % 2) {
myView.backgroundColor = [UIColor whiteColor];
} else {
myView.backgroundColor = [UIColor blackColor];
}
cell.backgroundView = myView;
No need to change the accessoryView or contentView background colors. They'll follow automatically.
Note for 2014. Very typically you wold use -(void)setSelected:(BOOL)selected animated:(BOOL)animated
So, you'd have a custom cell class, and you'd set the colours for the normal/selected like this...
HappyCell.h
#interface HappyCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *mainLabel;
etc...
#end
HappyCell.m
#implementation HappyCell
-(id)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
}
return self;
}
-(void)awakeFromNib
{
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
self.backgroundColor = [UIColor redColor];
.. other setup for selected cell
}
else
{
self.backgroundColor = [UIColor yellowColor];
.. other setup for normal unselected cell
}
}
#end
// to help beginners.......
// in your table view class, you'd be doing this...
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return yourDataArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger thisRow = indexPath.row;
ContentsCell *cell = [tv
dequeueReusableCellWithIdentifier:#"cellName"
forIndexPath:indexPath];
// "cellName" must be typed in on the cell, the storyboard
// it's the "identifier", NOT NOT NOT the restorationID
[cell setupForNumber: thisRow];
cell.mainLabel.text = yourDataArray[ thisRow ][#"whatever"];
cell.otherLabel.text = yourDataArray[ thisRow ][#"whatever"];
return cell;
}
hope it helps someone.
This worked for me:
cell.contentView.backgroundColor = [UIColor darkGreyColor];
For all lines with the same color
cell.backgroundColor = [UIColor colorWithRed:6.0/255.0 green:122.0/255.0 blue:145.0/255.0 alpha:1.0f];
For 2 colors
cell.backgroundColor = [UIColor colorWithRed:6.0/255.0 green:122.0/255.0 blue:145.0/255.0 alpha:1.0f];
if ((cell.backgroundColor = (indexPath.row % 2 == 0 ? [UIColor colorWithRed:6.0/255.0 green:122.0/255.0 blue:145.0/255.0 alpha:1.0f] : [UIColor colorWithRed:2.0/255.0 green:68.0/255.0 blue:80.0/255.0 alpha:1.0f]))){
cell.textLabel.textColor = [UIColor whiteColor];
}
For anyone else who might stumble on this and wants to set their UITableViewCell background to a pattern or texture rather than a solid color, you can do so like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"pattern.png"]];
}
The best option to have different backgrounds and what not would probably be to make your own TableViewCell implementation, in there you can put the logic to show whatever you want based on content or index etc.