minimalpop
minimalpop

Reputation: 7058

Getting row of UITableView cell on button press

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?

Upvotes: 85

Views: 72961

Answers (10)

Leszek Szary
Leszek Szary

Reputation: 10318

In swift:

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

Upvotes: 0

iwasrobbed
iwasrobbed

Reputation: 46703

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.

Upvotes: 278

Roy Falk
Roy Falk

Reputation: 1741

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()

Upvotes: 1

Anupam Mishra
Anupam Mishra

Reputation: 3588

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

Upvotes: 0

vietstone
vietstone

Reputation: 9092

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

Upvotes: 2

Ashok
Ashok

Reputation: 6244

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.

Upvotes: 18

user523234
user523234

Reputation: 14834

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;

Upvotes: 40

Mr. T
Mr. T

Reputation: 13055

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

Upvotes: 3

Jakob W
Jakob W

Reputation: 3377

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).

Upvotes: 0

Derek Li
Derek Li

Reputation: 3111

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.

Upvotes: 2

Related Questions