objective c action for button in custom collection view cell - objective-c

I've made custom collection view cell with image and button. How to create a method for this button by clicking on it I need to know what exactly cell was clicked.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
[self.myCollectionView registerNib:[UINib nibWithNibName:#"myGirlCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:#"CELL1"];
myGirlCollectionViewCell *cellGirl = [collectionView dequeueReusableCellWithReuseIdentifier:#"CELL1" forIndexPath:indexPath];
cellGirl.girlImg.image = [[_mainArray objectAtIndex:indexPath.row]objectForKey:#"img"];
NSString *name = [[_mainArray objectAtIndex:indexPath.row]objectForKey:#"name"];//the name I want to see in the method!
[cellGirl.btnName addTarget:self action:#selector(clickMe:) forControlEvents:UIControlEventTouchUpInside];
return cellGirl;
}
-(void)clickMe:(UIButton *)button{
//here *name
}
How should I give this parameter?

set tag for the button.
cellGirl.btnName.tag = indexPath.row;
Then inside this method.
-(void)clickMe:(UIButton *)button{
//here *name
//check for tag value like this
if (button.tag == 0)
{
// Your code here
}
}

Related

CALayer addSublayer does not work in didSelectItemAtIndexPath

I want to achieve an effect that can turn to red when a cell is clicked in CollectionView,so I use CAlayer,but it does not work. When I use target-action inside cell to achieve, it can work perfectly.
Here are the codes:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
videoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"videoCell" forIndexPath:indexPath];
CALayer *testLayer = [[CALayer layer] init];
testLayer.frame = cell.bounds;
testLayer.backgroundColor = [UIColor redColor].CGColor;
[cell.layer addSublayer:testLayer];
}
You need to use the cell's contentView to accomplish this. Also, remember cells are caches and so you can not use state that you store inside a cell. You have to store it somewhere else. And you should not use didSelectItemAtIndexPath to update the UI. Rather change the state there and then request and update of the relevant cell.
Here is a nice example to illustrate. In this example you can select multiple cells and they all will have a red background. The selection state of a cell is stored inside a dictionary in the controller. You could easily change this if you e.g. only want to have a single cell selected at a given moment in time. Then you need to also updated the cell that became unselected but that is another example.
#import "CollectionViewController.h"
#interface CollectionViewController () < UICollectionViewDelegate, UICollectionViewDataSource >
#property (nonatomic,strong) NSMutableDictionary * selectedCells; // Key is integer row, value is boolean selected
#end
#implementation CollectionViewController
static NSString * const reuseIdentifier = #"Cell";
- (void)viewDidLoad {
[super viewDidLoad];
// Register cell classes
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
// Do any additional setup after loading the view.
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
// Empty / nothing selected for now
self.selectedCells = NSMutableDictionary.dictionary;
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell based on state
if ( [( NSNumber * )[self.selectedCells objectForKey:#( indexPath.row )] boolValue] ) {
cell.contentView.backgroundColor = UIColor.redColor;
} else {
cell.contentView.backgroundColor = UIColor.blueColor;
}
return cell;
}
#pragma mark <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// Flip selected state
[self.selectedCells setObject:#( ! [( NSNumber * )[self.selectedCells objectForKey:#( indexPath.row )] boolValue] )
forKey:#( indexPath.row )];
// Request an UI update to reflect the updated state
[collectionView reloadItemsAtIndexPaths:#[ indexPath ]];
}
#end

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.

UICollectionViewCell NSindexpath reference position

How do I reference a specific UICollectionViewCell so that each of them segue to different VCs?
Here is the code for the cells:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MY_CELL" forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:self.myCellImages[0]];
UIImage *cellImage = [[UIImage alloc] init];
cellImage = [UIImage imageNamed:[self.myCellImages objectAtIndex:indexPath.row]];
cell.imageView.image = cellImage;
return cell;
}
So this displays a bunch of different cells depending on how many images I load in to my array.
say my array is:
self.myCellImages = #[#"1.jpg", #"2.jpg",.... #"20.jpg"];
how do I reference an individual cell so that it segues into different VC? For example, clicking on the cell that has image 1.jpg segues into 1VC and clicking on the cell with17.jpg segues to 17VC
Is it something like:
if (NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];)
{
//segue here
}
I can't figure out the correct syntax
Thanks to Timothy, I added
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:indexPath
{
NSString *storyboardId = #"main_to_VC1";
[self performSegueWithIdentifier:storyboardId sender:indexPath];
}
You need to establish segues between your collection view controller (not the cell) and the destination controllers, giving each a unique storyboard ID.
Then in the collectionView:didSelectItemAtIndexPath: delegate method, decide which controller you want to segue to based on the indexPath and initiate the segue programmatically:
- (void)collectionView:collectionView didSelectItemAtIndexPath:indexPath
{
NSString *storyboardId = ...; // determine storyboard ID based on indexPath
[self performSegueWithIdentifier:storyboardId sender:indexPath];
}

iOS custom UITableViewCell set UIButton title label text

I am making a chatting application.
In my datasource method, I dequeue a cell and pass a Core Data Entity to initialize it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Post *post = self.posts[indexPath.row];
AbstractPostCell *cell;
if ([post.type isEqualToString:#"text"]) {
cell = [tableView dequeueReusableCellWithIdentifier:CELL_POST_LIST_TEXT forIndexPath:indexPath];
}
else if ([post.type isEqualToString:#"image"]) {
cell = [tableView dequeueReusableCellWithIdentifier:CELL_POST_LIST_IMAGE forIndexPath:indexPath];
}
[cell setupWithPost:post];
return cell;
}
Here is setupWithPost method:
- (void)setupWithPost:(Post *)post {
self.label.text = post.text;
self.datetimeLabel.text = [Format stringWithDatetime:post.datetime];
if ([DBHelper didLikePost:post likerID:[Helper currentUserID]]) {
self.likeButton.enabled = NO;
self.likeButton.titleLabel.alpha = 0.5;
self.likeButton.titleLabel.text = [NSString stringWithFormat:#"liked"];
}
else {
self.likeButton.enabled = YES;
self.likeButton.titleLabel.alpha = 1.0;
self.likeButton.titleLabel.text = [NSString stringWithFormat:#"like"];
}
}
the button labels are correct when I first enter the ListViewController(TableViewController), but When I navigate to the next ViewController (DetailViewController), and then navigate back, all button labels become unset (showing whatever text I preset in storyboard)
Interestingly, when I scroll down and then scroll back, all the buttons labels become correct again.
I have tried prepareForReuse but not working
NOTE: this applies only to button title labels, normal labels (e.g. datetimeLabel) are always correctly displayed
- (void)prepareForReuse {
[super prepareForReuse];
self.label.text = #"";
self.datetimeLabel.text = #"";
self.likeButton.titleLabel.text = #"";
}
that sound very strange, I don't know if this is the cause of the problem but you should set a button title using the API
- (void)setTitle:(NSString *)title forState:(UIControlState)state
try
[self.likeButton setTitle:#"some title" forState:UIControlStateNormal];

Default CellAccessory

How can I set first row of tableview checkmarked when the app started? I mean when I start app now, there are few rows in tableview and when I click on some of them its accessory change to checkmark. When click another one, the another one comes checkmarked and previous checkmark dissapears. So now I want first row of tableview to be checkmarked when app is started and then when I click another row it 'uncheckmark' and 'checkmark' new row etc...
Try the options suggested in these posts how to apply check mark on table view in iphone using objective c? or iPhone :UITableView CellAccessory Checkmark
Basically you need to keep track of the checkmarks using say a dictionary or so. And In viewDidLoad or init method make the first cell as checked in the dictionary. While drawing cells, always check if the corresponding entry in the dictionary is checked or not and display check mark accordingly. When user taps on a cell, modify the value in dictionary to checked/unchecked.
Update:
Here is a sample code.
In .h file declare a property as
#property(nonatomic) NSInteger selectedRow;
Then use the below code,
- (void)viewDidLoad {
//some code...
self.selectedRow = 0; //if nothing should be selected for the first time, then make it as -1
//create table and other things
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// create cell and other things here
if (indexPath.row == self.selectedRow)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// some other code..
if (indexPath.row != self.selectedRow) {
self.selectedRow = indexPath.row;
}
[tableView reloadData];
}
You could set an integer to a variable similar to "checkedCell", have that value default to cell 0, and in the cellForRowAtIndexPath check to see if the cell is already checked, if not change the accessory to make it checked and update your variable.
-(void)viewWillAppear{
currentCheckedCell = 0;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
// Create cells -- if indexpath.row is equal to current checked cell reload the table with the correct acessories.
//
static NSString *cellIdentifier = #"RootMenuItemCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
for (UIView *view in nibContents) {
if ([view isMemberOfClass:[MyCell class]]) {
cell = (MyCell *)view;
break;
}
}
//OTHER CELL CREATION STUFF HERE
// cell accessory
UIImage *accessory = [UIImage imageNamed:#"menu-cell-accessory-default.png"];
UIImage *highlighted = [UIImage imageNamed:#"menu-cell-accessory-selected.png"];
if(indexPath.row == currentCheckedCell){
//DEFAULT ACCESSORY CHECK
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:highlighted];
}else{
//DEFAULT ACCESSORY NONE
// cell.accessoryType = UITableViewCellAccessoryNone;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:accessory];
}
[cell setAccessoryView:accessoryView];
}
return cell;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//UPDATE SELECTED CELL & RELOAD TABLE
currentCheckedCell = indexPath.row;
[self.tableview reloadData];
}
Also worth noting that my examples uses custom images for accessories.
If you check out the link #ACB provided you'll find a very similar concept.
You can use,
if (firstCellSelected)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Set the firstCellSelected flag in viewDidLoad method.