12

I have a UITableview with multiple cells. On each cell I'm adding buttons dynamically according to some array count. So, when I click on a button I'm able to get the tag value of the button. But how to get the indexPath of that cell?

Here is my code in -cellForRowAtIndexPath:

   UIView *view=(UIView*)[cell.contentView viewWithTag:indexPath.row+444];

   UIImageView *img=(UIImageView*)[cell.contentView viewWithTag:indexPath.row+999];
   img.image=[UIImage imageNamed:@"BHCS_empty.png"];

   if(integer!=50)
   {
        NSInteger y_axis=0;
        NSArray *Arr=[tableSubCategoryArr objectAtIndex:indexPath.row];
        img.image=[UIImage imageNamed:@"BHCS_selected.png"];

        view.Frame= CGRectMake(0,50,281, integer-50);
        for (int i=0; i<[Arr count]; i++)
        {
            NSLog(@"arr %@",[Arr objectAtIndex:i]);
            UIButton *Btn=[UIButton buttonWithType: UIButtonTypeCustom];
            Btn.frame=CGRectMake(0, y_axis, 281, 44);
            [Btn setImage:[UIImage imageNamed:@"BHCS_panel.png"] forState:UIControlStateNormal];
            [Btn addTarget:self action:@selector(subCategoryBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
            [Btn setTag:i+100];
            [view addSubview:Btn];


            UILabel *nameLbl=[[UILabel alloc]initWithFrame:CGRectMake(20, y_axis,248, 44)];
            nameLbl.text = [[Arr objectAtIndex:i]objectForKey:@"SubCategoryName"];
            nameLbl.textColor = [UIColor whiteColor];
            nameLbl.backgroundColor=[UIColor clearColor];
            panelTableView.separatorColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"BHCS_panel_div1.png"]];
            nameLbl.font = [UIFont fontWithName:@"Helvetica" size:12.0f];
            [view addSubview:nameLbl];

            y_axis=y_axis+44+1.3f;
        }
    }
Ortwin Gentz
  • 52,648
  • 24
  • 135
  • 213
user2230971
  • 287
  • 1
  • 7
  • 22
  • An easy way to do this is create a UITableView extension. See my answer to [this question](https://stackoverflow.com/a/39059239/3395921) to see how it can be done. – Ishan Handa Aug 20 '16 at 22:33

8 Answers8

29

I have tried maximum of given answers, but at the and I generally use to go for most Generalised and ideal way as follows:

 CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
 NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
Mrug
  • 4,963
  • 2
  • 31
  • 53
  • Super ... In Swift let hitpoint = sender.convert(CGPoint.zero, to: self.tablename) let indexpath_select = self.Flight_detail_table.indexPathForRow(at: hitpoint) print("indexpath: \(String(describing: indexpath_select))") – Hari Narayanan Jan 03 '18 at 13:14
13

ALLLLL of the answers here are bad and you shouldn't be looping through superviews. Classic example, with iOS 7 Apple changed the tableViewCell hierarchy and your app will now crash!

Use this instead:

How to know the UITableview row number

Community
  • 1
  • 1
CW0007007
  • 5,681
  • 4
  • 26
  • 31
  • oh. This was such a short, link-only answer I almost missed, but it is actually really good. The point is that the button contains information about where it was on the screen, which can be readily converted to an index path. – Suz Jul 21 '15 at 22:01
  • Do you want to qualify "bad"? A fixed dependency on [someView superview] might break over time (even if the whole view hierarchy is controlled by the app developer). But looping up until one finds the table view cell is certainly safe now and in the future. – danh Jan 10 '17 at 15:47
10

Updated answer

Use it like:

CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView]; 
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
Amit
  • 1,043
  • 1
  • 10
  • 32
  • it is crashing at NSIndexPath *indexPath = [view indexPathForCell:cell]; – user2230971 Apr 26 '13 at 06:10
  • Are you adding your button directly into cell? If not you need to go to superview till you reach cell like button.superview.superview and so on. Please check the type of cell while debugging, it will help. – Amit Apr 26 '13 at 06:31
  • 4
    NO, NO NO NO ! Just don't loop through superviews ! http://stackoverflow.com/questions/9274494/how-to-know-the-uitableview-row-number/9274863#9274863 – CW0007007 Jan 30 '14 at 15:24
  • i was getting the same thing. looks like in iOS 7, the hierarchy changed as apple added UITableViewCellScrollView: http://www.curiousfind.com/blog/646 so you need to add one more .superview. @CW0007007 that does work but only if you have the self.tableview property and my screen happens to have a whole bunch of tableviews making it trickier. – skinsfan00atg Apr 10 '14 at 14:49
  • in iOS8..this will always give 0. Please see this....http://stackoverflow.com/questions/23784630/how-to-find-indexpath-for-tapped-button-in-tableview-using-seque/27292570#27292570 – Mayank Jain Dec 04 '14 at 11:15
  • Do not use this code. Never assume such a subview hierarchy. It will crash. – rmaddy Aug 16 '17 at 15:26
7

Thanks to all i slove this by using below code

     NSIndexPath *indexPath = [panelTableView indexPathForCell:(UITableViewCell *)sender.superview.superview];

NSLog(@"%d",indexPath.row);
user2230971
  • 287
  • 1
  • 7
  • 22
  • 3
    in iOS8..this will always give 0. Please see this....http://stackoverflow.com/questions/23784630/how-to-find-indexpath-for-tapped-button-in-tableview-using-seque/27292570#27292570 – Mayank Jain Dec 04 '14 at 11:15
  • 1
    Do not use this answer. It is wrong. Never assume a specific structure for the private view hierarchy. – rmaddy Aug 16 '17 at 15:21
0

Put this code in the button's action method:

NSIndexPath *indexPath = [yourtableviewname indexPathForCell:(UITableViewCell *)sender.superview];
   NSLog(@"%d",indexPath.row);
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Balu
  • 8,470
  • 2
  • 24
  • 41
0

I suggest adding a property to your custom UITableViewCell implementation class to store the indexPath. Then, when your cellForRowAtIndexPath delegate fires in your TableViewController, set the indexPath property for that cell.

customTableViewCell.h :

@interface customTableViewCell : UITableViewCell

@property NSIndexPath *indexPath;

@end

customTableViewController configure cell:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    customTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"customTableViewCell" forIndexPath:indexPath];
    // Do whatever you want with your cell
    cell.indexPath = indexPath;

    return cell;
}

Then you can refer to the indexPath property in your customTableViewCell by calling self.indexPath

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
cacodev
  • 163
  • 11
  • 1
    No. do not do this. If the table view allows rows to be deleted, inserted, or moved, then the `indexPath` property of this custom cell will be wrong. – rmaddy Aug 16 '17 at 15:23
0

None of these answers seem like a very clean solution. The way I would implement this is by using the delegate pattern. The view controller is the cell's delegate, meaning you let the cell itself handle the button press, and it tells its delegate when the button was pressed so it can handle it however it wants.

Let's say you have a tableview where each cell represents a Person object, and when the button is pressed you want to show a profile for this person. All you need to do is this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    PersonCell *cell = [tableView dequeueReusableCellWithIdentifier:@"customTableViewCell" forIndexPath:indexPath];
    cell.delegate = self;
    cell.person = self.people[indexPath.row];
    return cell;
}

- (void)personCell:(PersonCell *)personCell didPressButtonForPerson:(Person *)person {
    [self showProfileForPerson:person];
}

Then all you need to do in your button class is add a property called buttonPressedHandler that is a block passing back an instance of Person, and when you create your button and add the target do something like this:

- (void)viewDidLoad {
    [super viewDidLoad];

    // do whatever whatever else you need/want to here

    [self.button addTarget:self selector:@selector(handleButtonPressed) forState:UIControlStateNormal];
}

- (void)handleButtonPressed {
    // Make sure this is a required delegate method, or you check that your delegate responds to this selector first...
    [self.delegate personCell:self didPressButtonForPerson:self.person];
}
Mike
  • 9,765
  • 5
  • 34
  • 59
-1

You can use UITableView's indexPathForCell: method like so [tableView indexPathForCell:cell];. Good Luck!

Fahri Azimov
  • 11,470
  • 2
  • 21
  • 29