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:
- I press the first cell button: buttonPressed is called but indexPath is nil, so nothing happens.
- I press the third cell button: buttonPressed is called and the cell is removed as expected.
- I press the first cell again: same as before, buttonPressed called but indexPath is nil.
- I press the second cell button: buttonPressed is called and the cell is removed as expected.
- 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
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
}
}
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?
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 .. :)
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...
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
}
}