在按钮上获取 UITableView 单元格行

我有一个 tableview 控制器,它显示一行单元格。每个单元格有3个按钮。我已经为每个单元格的标签编号为1,2,3。问题是我不知道如何找到正在按下的按钮。我目前只得到发件人的标签时,其中一个按钮已被按下。当按下按钮时,是否也有方法获取单元格行号?

72574 次浏览

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.

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

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;

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

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.

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.

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

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

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

In swift:

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