9

I have a UIButton inside of a custom UITableViewCell that removes the cell when the button is pressed. In order to get the indexPath of the cell the button is in, I'm calling indexPathForRowAtPoint: in a method called when the button is pressed as follows:

-(void)buttonPressed:(UIButton *)sender{
    CGPoint btnPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:btnPosition];

    if (indexPath != nil)
    {
    //remove the cell from the tableview. 
    }
}

This works fine if I press the button in any cell except the first cell, and also when if there is ONLY one cell in the table. However, if there are multiple cells and I press the button in the first cell, the button press is triggered and the btnPosition is found, but [self.tableView indexPathForRowAtPoint:btnPosition]; returns nil.

For example, if there are three cells, each with a button:

  1. I press the first cell button: buttonPressed is called but indexPath is nil, so nothing happens.
  2. I press the third cell button: buttonPressed is called and the cell is removed as expected.
  3. I press the first cell again: same as before, buttonPressed called but indexPath is nil.
  4. I press the second cell button: buttonPressed is called and the cell is removed as expected.
  5. I press the first cell button: This time, buttonPressed is called, indexPath is not nil and the cell is removed as expected.

I've checked that the tableView is not nil, as suggested by a related, but different, post. I've also confirmed that the point btnPosition is set to the same value (x=260, y=44.009995) both when [self.tableView indexPathForRowAtPoint:btnPosition] == nil AND when [self.tableView indexPathForRowAtPoint:btnPosition] != nil.

So aside from asking if anyone has any ideas on what could be happening here, my question is:

Under what circumstances could passing the same CGPoint to [self.tableView indexPathForRowAtPoint:btnPosition] return a different (or nil) indexPath?

I'd appreciate help with any ideas of where I might look to track this down, or to hear if anyone has encountered a similar issue.

Some additional notes that might be helpful (please excuse if I make a question asking faux-pas. I'll happily take your feedback about that as well :)

  • This tableView is part of a UITableViewController which is embedded in a UINavigationController

  • The tableView has 3 sections, 2 of which are hidden from view (0 rows, no footer, no header) while I'm presenting the section with the button cell rows as described.

  • I'm adjusting the location of the buttons during presentation by programmatically changing their horizontal constraints.

  • The heights of my customUITableViewCell, tableViewRows and UIButton are each equal to 60.0f

riocordero
  • 119
  • 1
  • 5
  • 3
    As an experiment, try using `CGPointMake(5,5)` instead of `CGPointZero`. Does that make any difference? – rmaddy Mar 13 '14 at 03:39
  • 1
    BTW - if that little experiment doesn't work, please update your question with two screenshots. One from when it works and one from when it doesn't. – rmaddy Mar 13 '14 at 03:41
  • Hm, I heart SO. Thanks rmaddy, that seems to do the trick, can you elaborate a bit on what I was doing wrong? I'm happy to have a fix but would love to know why. – riocordero Mar 13 '14 at 04:00
  • I haven't tested but you might get a nil indexPath for a section header or footer if you pass a CGPoint in them. – Robotic Cat Mar 13 '14 at 04:01
  • Given that you were using `CGRectZero` and the fact that the `y` coordinate of `btnPosition` was a strange value (`44.009995` instead of `44`), I suspected you may have either had an edge case or been victim to roundoff error. By choosing a point that wasn't in the absolute corner of the view, you had a better chance of avoiding roundoff issues. – rmaddy Mar 13 '14 at 04:05
  • Ah, of course, that makes sense now. My section header height is '44' and the section above it is hidden but that section's header was actually set to height of '0.01f'. Thanks for explaining, I appreciate it! – riocordero Mar 13 '14 at 04:26

5 Answers5

6

Just try this:

fix target selector:

[button addTarget:self action:@selector(buttonPressed:event:)forControlEvents:UIControlEventTouchUpInside];


- (void)buttonPressed:(id)sender event:(id)event{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchPos = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:touchPos];
    if(indexPath != nil){
        //do operation with indexPath
    }
}
simalone
  • 2,768
  • 1
  • 15
  • 20
5

I feel awful that I will get points for this, but @rmaddy's comment worked wonders for my code. I want to put this as an answer since I missed his comment the first time round and spent ages implementing a longer fix. Thanks rmaddy for the fix:

As an experiment, try using CGPointMake(5,5) instead of CGPointZero. Does that make any difference?

rmaddy then explained why it worked:

Given that you were using CGRectZero and the fact that the y coordinate of btnPosition was a strange value (44.009995 instead of 44), I suspected you may have either had an edge case or been victim to roundoff error. By choosing a point that wasn't in the absolute corner of the view, you had a better chance of avoiding roundoff issues.

rmaddy - this issue has been bugging me for 2 weeks... and you fixed it. Thank you very much. Can I donate any points on stackoverflow to you?

Community
  • 1
  • 1
Charlie S
  • 4,366
  • 6
  • 59
  • 97
1

I am going to share the way I do this type of functionality. This will work if you have one section. You don't need these 2 lines:

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

In your cellForRowAtIndexPath while adding a custom cell add these line:

cell.btn.tag = indexPath.row;
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];

And in buttonPressed do this:

-(void)buttonPressed:(UIButton *)sender{
    UIButton *btn = (UIButton *)sender;
    //Remove the btn.tag number row.
}

For more than 1 section you can do this:

-(void)buttonPressed:(UIButton *)sender{
    UIButton *btn = (UIButton *)sender;
    UITableViewCell *cell = (UITableViewCell *)btn.superview;
    UITableView *tableView = (UITableView *)cell.superview;
    NSIndexPath *indexPath = [tableView indexPathForCell:cell];
    //Remove this cell in indePath
}

Hope this helps .. :)

Rashad
  • 11,057
  • 4
  • 45
  • 73
1

Add UITapGestureRecognizer to UIButton as well as add a target Method for GestureObject.

UITapGestureRecognizer *tapGestureDelete=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(callAMethod:)];
    [yourDeleteButton addGestureRecognizer:tapGestureDelete];

In taget callAMethod retrieve the gestured position as below

-(void)callAMethod:(UIGestureRecognizer *)gestureRecognizer
{
NSIndexPath *currentIndexPath = [self.tableView indexPathForRowAtPoint:[gestureRecognizer locationInView:self.talbleView]];
    if(currentIndexPath != nil) 
   { 
       // remove the cell from tableview 
   }
}

I Hope It will work...

0

simalone's answer in Swift (as of version 2.2) will be:

tableViewCell.button.addTarget(self, action: #selector(buttonPressed(_:event:)), forControlEvents: .TouchUpInside)
...
@IBAction func buttonPressed(sender: UIButton, event: UIEvent) {
    if let touchPos = event.allTouches()?.first?.locationInView(self.TableView),
       let indexPath = self.TableView.indexPathForRowAtPoint(touchPos)?.row {
            // do operations with indexPath
    }
}
Vitalii
  • 4,267
  • 1
  • 40
  • 45