Getting row of UITableView cell on button press

Objective CIosUitableview

Objective C Problem Overview


I have a tableview controller that displays a row of cells. Each cell has 3 buttons. I have numbered the tags for each cell to be 1,2,3. The problem is I don't know how to find on which cell a button is being pressed. I'm currently only getting the sender's tag when one of the buttons has been pressed. Is there a way to get the cell row number as well when a button is pressed?

Objective C Solutions


Solution 1 - Objective C

You should really be using this method instead:

CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];

Swift version:

let buttonPosition = sender.convert(CGPoint(), to:tableView)
let indexPath = tableView.indexPathForRow(at:buttonPosition)

That will give you the indexPath based on the position of the button that was pressed. Then you'd just call cellForRowAtIndexPath if you need the cell or indexPath.row if you need the row number.

If you're paranoid, you can check for if (indexPath) ... before using it just in case the indexPath isn't found for that point on the table view.

All of the other answers are likely to break if Apple decides to change the view structure.

Solution 2 - Objective C

Edit: This answer is outdated. Please use this method instead


Try this:

-(void)button1Tapped:(id)sender
{
    UIButton *senderButton = (UIButton *)sender;
    UITableViewCell *buttonCell = (UITableViewCell *)[senderButton superview];
    UITableView* table = (UITableView *)[buttonCell superview];
    NSIndexPath* pathOfTheCell = [table indexPathForCell:buttonCell];
    NSInteger rowOfTheCell = [pathOfTheCell row];
    NSLog(@"rowofthecell %d", rowOfTheCell);
}

Edit: If you are using contentView, use this for buttonCell instead:

UITableViewCell *buttonCell = (UITableViewCell *)senderButton.superview.superview;

Solution 3 - Objective C

I would recommend this way to fetch indexPath of cell which has any custom subview - (compatible with iOS 7 as well as all previous versions)

-(void)button1Tapped:(id)sender {
//- (void)cellSubviewTapped:(UIGestureRecognizer *)gestureRecognizer {
//    UIView *parentCell = gestureRecognizer.view.superview;
    UIView *parentCell = sender.superview;

    while (![parentCell isKindOfClass:[UITableViewCell class]]) {   // iOS 7 onwards the table cell hierachy has changed.
        parentCell = parentCell.superview;
    }

    UIView *parentView = parentCell.superview;

    while (![parentView isKindOfClass:[UITableView class]]) {   // iOS 7 onwards the table cell hierachy has changed.
        parentView = parentView.superview;
    }


    UITableView *tableView = (UITableView *)parentView;
    NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)parentCell];

    NSLog(@"indexPath = %@", indexPath);
}

This doesn't require self.tablview either.

Also, notice the commented code which is useful if you want the same through a @selector of UIGestureRecognizer added to your custom subview.

Solution 4 - Objective C

There are two ways:

  1. @H2CO3 is right. You can do what @user523234 suggested, but with a small change, to respect the UITableViewCellContentView that should come in between the UIButton and the UITableViewCell. So to modify his code:

     - (IBAction)button1Tapped:(id)sender
     {
         UIButton *senderButton = (UIButton *)sender;
         UITableViewCellContentView *cellContentView = (UITableViewCellContentView *)senderButton.superview;
         UITableViewCell *tableViewCell = (UITableViewCell *)cellContentView.superview;
         UITableView* tableView = (UITableView *)tableViewCell.superview;
         NSIndexPath* pathOfTheCell = [tableView indexPathForCell:tableViewCell];
         NSInteger rowOfTheCell = pathOfTheCell.row;
         NSLog(@"rowofthecell %d", rowOfTheCell);
     }
    
  2. If you create a custom UITableViewCell (your own subclass), then you can simply call self in the IBAction. You can link the IBAction function to your button by using storyboard or programmatically when you set up the cell.

     - (IBAction)button1Tapped:(id)sender
     {
         UITableView* tableView = (UITableView *)self.superview;
         NSIndexPath* pathOfTheCell = [tableView indexPathForCell:self];
         NSInteger rowOfTheCell = pathOfTheCell.row;
         NSLog(@"rowofthecell %d", rowOfTheCell);
     }
    

Solution 5 - Objective C

I assume you add buttons to cell in cellForRowAtIndexPath, then what I would do is to create a custom class subclass UIButton, add a tag called rowNumber, and append that data while you adding button to cell.

Solution 6 - Objective C

Another simple way:

  • Get the point of touch in tableView

  • Then get index path of cell at point

  • The index path contains row index

The code is:

- (void)buttonTapped:(id)sender {
    UITapGestureRecognizer *tap = (UITapGestureRecognizer *)sender;
    CGPoint point = [tap locationInView:theTableView];

    NSIndexPath *theIndexPath = [theTableView indexPathForRowAtPoint:point];

    NSInteger theRowIndex = theIndexPath.row;
    // do your stuff here
    // ...
}

Solution 7 - Objective C

Swift 3

Note: This should really go in the accepted answer above, except that meta frowns upon such edits.

@IBAction func doSomething(_ sender: UIButton) {
   let buttonPosition = sender.convert(CGPoint(), to: tableView)
   let index = tableView.indexPathForRow(at: buttonPosition)
}

Two minor comments:

  1. The default function has sender type as Any, which doesn't have convert.
  2. CGPointZero can be replaced by CGPoint()

Solution 8 - Objective C

One solution could be to check the tag of the button's superview or even higher in the view hierarchy (if the button is in the cell's content view).

Solution 9 - Objective C

I would like to share code in swift -

extension UITableView
{
    func indexPathForCellContainingView(view1:UIView?)->NSIndexPath?
    {
        var view = view1;
        while view != nil {
            if (view?.isKindOfClass(UITableViewCell) == true)
            {
                return self.indexPathForCell(view as! UITableViewCell)!
            }
            else
            {
                view = view?.superview;
            }
        }
        return nil
    }
}

Solution 10 - Objective C

In swift:

@IBAction func buttonAction(_ sender: UIButton) {
    guard let indexPath = tableView.indexPathForRow(at: sender.convert(CGPoint(), to: tableView)) else {
        return
    }
    // do something
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionminimalpopView Question on Stackoverflow
Solution 1 - Objective CiwasrobbedView Answer on Stackoverflow
Solution 2 - Objective Cuser523234View Answer on Stackoverflow
Solution 3 - Objective CAshokView Answer on Stackoverflow
Solution 4 - Objective CMr. TView Answer on Stackoverflow
Solution 5 - Objective CDerek LiView Answer on Stackoverflow
Solution 6 - Objective CvietstoneView Answer on Stackoverflow
Solution 7 - Objective CRoy FalkView Answer on Stackoverflow
Solution 8 - Objective CJakob WView Answer on Stackoverflow
Solution 9 - Objective CAnupam MishraView Answer on Stackoverflow
Solution 10 - Objective CLeszek SzaryView Answer on Stackoverflow