Get correct button from custom UICollectionViewCell - objective-c

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.

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

objective c action for button in custom collection view cell

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
}
}

NSTableViewCell selectedRow number for IBAction click

I'm running into a simple problem but have yet to find an optimal solution. I have a view based NSTableView that is loading it's cell views from different xibs. My table view is dynamic and based on user input I will dynamically add and remove rows ultimately adjusting the table data source. Each one of my NSTableCellViews have a button in it and I link the IBAction click handler to the NSView that holds the table view. What I need to do is get the row number for the button that was clicked in the table view so I can process the logic. I am able to do this successfully in : tableViewSelectionDidChange:(NSNotification *)notification
Here is how I do it:
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *tableView = [notification object];
NSInteger selectedRow = [tableView selectedRow];
}
This works perfectly for a user actually clicking the row. Now when I move the NSButton IBAction and link it in the NSView as follows:
- (IBAction)buttonClickHandler:(NSButton *)sender {
NSInteger selectedRow = [self.tblView rowForView:sender];
NSLog(#"%ld", (long)selectedRow);
}
I based this approach from this selected answer.
I also tried this:
- (IBAction)buttonClickHandler:(NSButton *)sender {
id representedObject = [(NSTableCellView *)[sender superview] objectValue];
NSLog(#"%#", representedObject);
}
//My configuration
- (void)configureView {
[self.view setFrame:[self bounds]];
[self addSubview:self.view];
[self.view setWantsLayer:YES];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.tblView.delegate = self;
self.tblView.dataSource = self;
[self.tblView setIntercellSpacing:NSMakeSize(0, 0)];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"ParentCellXib" bundle:nil] forIdentifier:#"ParentCell"];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"ChildCellXib" bundle:nil] forIdentifier:#"ChildCell"];
[self.tblView registerNib: [[NSNib alloc] initWithNibNamed:#"HeaderCellXib" bundle:nil] forIdentifier:#"HeaderCell"];
}
But the represented object returns null. If it's worth mentioning, I've set my File's Owner as the View that holds the tableView so I can link the IBAction and I've subclassed the TableCellView to a different class. However, I don't think this is part of the problem as far as I can see. Is there a simple solution to reliably give me the selectedRow number based on a button click in that cell? Both approaches I tried above return -1 and null respectively.
I would set the row in NSButton's tag property:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
SomeTableCellView *cell = [tableView makeViewWithIdentifier:#"cell" owner:self];
if (cell == nil) {
cell = // init some table cell view
cell.identifier = #"cell";
}
cell.button.tag = row;
[cell.button setTarget:self];
[cell.button setAction:#selector(buttonAction:)];
}
- (IBAction)buttonAction:(id)sender {
NSLog(#"row: %d", sender.tag);
}
Try This
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
yourCustomeCell *aCell;
NSString *aStrIdentifier = #"yourIdentiFier";
aCell = (yourCustomeCell *)[tableView dequeueReusableCellWithIdentifier:aStrIdentifier];
//you have to set your indexpath
objc_setAssociatedObject(aCell.btnUpload_or_Add, #"objBtn", indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[aCell.YourButton addTarget:self action:#selector(yourButtonActiontapped:) forControlEvents:UIControlEventTouchUpInside];
return aCell;
}
-(IBAction)yourButtonActiontapped:(UIButton *)sender{
NSIndexPath *aIndPath = objc_getAssociatedObject(sender, #"objBtn");
NSLog(#"row:%#",aIndPath.row);
}
also you have to import #import <objc/runtime.h>
another way to get row in IBAction is TAG but objc is better option insted of TAG.
Create a subclass of UIButton and add a property for NSIndexPath for the button. Use this button in cellForRowAtIndexPath method. assign the index path of the cell to that of index path of the button.
On Tap, get the index path from its sender. In your case index path of that button.

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;
}

Objective c - Best practice to handle a button touch event for a button of a custom UITableViewCell

What is best practice to handle a button touch event for a button of a custom UITableViewCell?
my classes:
MyViewController, MyCustomCell
I can think of three options:
First option- Have the button as a property of MyCustomCell, and then add a target to it in the MyViewController .m file with MyViewController as the target.
MyViewController .m file
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"customCell";
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[cell.theButton addTarget:self
action:#selector(theButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)theButtonTapped:(UIButton *)sender
{
MyCustomCell *selectedCell = (MyCustomCell *)sender.superview;
if (selectedCell) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell];
MyModel *selectedModel = [self.model objectAtIndex:indexPath.row];
// do something with the model...
}
}
Second option- If the custom cell was made in IB, Set the nib File's Owner to be MyViewController, implement buttonTapped: method in MyViewController and connect the button's Touch Up Inside event to the buttonTapped: method.
Third option- if the custom cell wasn't made in IB, add a target to the button in the MyCustomCell .m file with MyCustomCell as the target.
Define a MyCustomCellDelegate add #property (nonatomic, assign) id<MyCustomCellDelegate> delegate to MyCustomCell and call this delegate when button tapped.
Set MyViewController as the cell's delegate when creating cells and implement the MyCustomCellDelegate protocol.
MyCustomCell .h file
#class MyCustomCell;
#protocol MyCustomCellDelegate <NSObject>
- (void)buttonTappedOnCell:(MyCustomCell *)cell;
#end
#interface MyCustomCell : UITableViewCell
#property (nonatomic, retain) UIButton *theButton;
#property (nonatomic, assign) id<MyCustomCellDelegate> delegate;
#end
MyCustomCell .m file
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.theButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.theButton.frame = CGRectMake(10,10,50,30);
[self addSubview:self.theButton];
[self.theButton addTarget:self
action:#selector(theButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)theButtonTapped:(UIButton *)sender
{
[self.delegate buttonTappedOnCell:self];
}
MyViewController .m file
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"customCell";
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.delegate = self;
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)buttonTappedOnCell:(MyCustomCell *)selectedCell
{
if (selectedCell) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell];
MyModel *selectedModel = [self.model objectAtIndex:indexPath.row];
// do something with the model...
}
}
Store the row of the cell as tag property of your custom button.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// bla bla bla
if (!cell)
{
//bla bla bla
[cell.yourButton addTarget:self selector:#selector(yourButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
// bla bla bla
cell.yourButton.tag = indexPath.row;
}
-(void)yourButtonTapped:(id)sender
{
int tag = [(UIButton *)sender tag];
NSLog(#"tapped button in cell at row %i", tag);
}
Using tag, from my point of view, would break the strictness of your code. In addition, when you have multiple sections, using tag definitely would make a mess of your code.
To avoid this problem, you can subclass UITableViewCell and make it hold an indexPath property to let the cell know its precise position.
Another problem here is, if UITableView invokes API to insert or delete row, you have to update visible cells' position data
I don't think that is the best practice.
There exists a better way.
I strongly recommend to use MVVM when you have to handle different touch events in your Cell.
In this pattern, your custom UITableViewCell would hold a custom CellViewModel. This class would be responsible for holding all data you associate with the cell, so you can retrieve the data and put the event handling logic inside the cell.
I have implemented block based approach by subclassing UIButton :
typedef void (^ActionBlock)(id sender);
#interface UIBlockButton : UIButton {
ActionBlock _actionBlock;
}
-(void)handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action;
​#end
#implementation UIBlockButton
-(void) handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action
{
_actionBlock = action;
[self addTarget:self action:#selector(callActionBlock:) forControlEvents:event];
}
-(void) callActionBlock:(id)sender{
_actionBlock(sender);
}
#end
And in tableview delegate :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!cell)
{
cell.yourButton.tag = indexPath.row;// pass tag
[cell.yourButton handleControlEvent:UIControlEventTouchUpInside withBlock:^(id sender) {
// your selector action code
NSLog(#"tapped button in cell at row %i",[(UIButton *)sender tag]);
}];
}
}
At some point your button is tapped, and at that point it is a subview of a cell which is a subview of some tableview.
Just write a method that takes a view, goes up the superview chain to find the containing cell, goes further up to find the tableview, and then asks the tableview for the indexPath of the cell.
That is a lot easier and more reliable than storing a tag containing a row, because you don't run into problems when tableview is edited, and it's much better to have code that finds out which indexPath it is when you need the indexPath, and not in some completely unrelated code when the cell is created.
Swift 3.0 Solution
cell.btnRequest.tag = indexPath.row
cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside)
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}