I have a UITableView
with 5 UITableViewCells
. Each cell contains a UIButton
which is set up as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
[cell autorelelase];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
[button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
[button setTag:1];
[cell.contentView addSubview:button];
[button release];
}
UIButton *button = (UIButton *)[cell viewWithTag:1];
[button setTitle:@"Edit" forState:UIControlStateNormal];
return cell;
}
My question is this: in the buttonPressedAction:
method, how do I know which button has been pressed. I've considered using tags but I'm not sure this is the best route. I'd like to be able to somehow tag the indexPath
onto the control.
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.
}
What's the standard way of doing this?
Edit:
I've kinda solved it by doing the following. I would still like to have an opinion whether this is the standard way of doing it or is there a better way?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
[cell autorelelase];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
[button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
[button release];
}
UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0];
[button setTag:indexPath.row];
[button setTitle:@"Edit" forState:UIControlStateNormal];
return cell;
}
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
int row = button.tag;
}
What's important to note is that I can't set the tag in the creation of the cell since the cell might be dequeued instead. It feels very dirty. There must be a better way.
A slight variation on Cocoanuts answer (that helped me solve this) when the button was in the footer of a table (which prevents you from finding the 'clicked cell':
SWIFT 2 UPDATE
Here's how to find out which button was tapped + send data to another ViewController from that button's
indexPath.row
as I'm assuming that's the point for most!For those who are using a ViewController class and added a tableView, I'm using a ViewController instead of a TableViewController so I manually added the tableView in order to access it.
Here is the code for passing data to another VC when tapping that button and passing the cell's
indexPath.row
This problem has two parts:
1) Getting the index path of
UITableViewCell
which contains pressedUIButton
There are some suggestions like:
Updating
UIButton
'stag
incellForRowAtIndexPath:
method using index path'srow
value. This is not an good solution as it requires updatingtag
continuously and it does not work with table views with more than one section.Adding an
NSIndexPath
property to custom cell and updating it instead ofUIButton
'stag
incellForRowAtIndexPath:
method. This solves multiple section problem but still not good as it requires updating always.Keeping a weak refence to parent
UITableView
in the custom cell while creating it and usingindexPathForCell:
method to get the index path. Seems a little bit better, no need to update anything incellForRowAtIndexPath:
method, but still requires setting a weak reference when the custom cell is created.Using cell's
superView
property to get a reference to parentUITableView
. No need to add any properties to the custom cell, and no need to set/update anything on creation/later. But cell'ssuperView
depends on iOS implementation details. So it can not be used directly.But this can be achieved using a simple loop, as we are sure the cell in question has to be in a UITableView:
So, these suggestions can be combined into a simple and safe custom cell method for getting the index path:
From now on, this method can be used to detect which
UIButton
is pressed.2) Informing other parties about button press event
After internally knowing which
UIButton
is pressed in which custom cell with exact index path, this information needs to be sent to other parties (most probably the view controller handling theUITableView
). So, this button click event can be handled in a similar abstraction and logic level todidSelectRowAtIndexPath:
method of UITableView delegate.Two approaches can be used for this:
a) Delegation: custom cell can have a
delegate
property and can define a protocol. When button is pressed it just performs it's delegate methods on it'sdelegate
property. But thisdelegate
property needs to be set for each custom cell when they are created. As an alternative, custom cell can choose to perform its delegate methods on it's parent table view'sdelegate
too.b) Notification Center: custom cells can define a custom notification name and post this notification with the index path and parent table view information provided in
userInfo
object. No need to set anything for each cell, just adding an observer for the custom cell's notification is enough.It works for me aswell, Thanks @Cocoanut
I found the method of using the superview's superview to obtain a reference to the cell's indexPath worked perfectly. Thanks to iphonedevbook.com (macnsmith) for the tip link text
It's simple; make a custom cell and take a outlet of button
change id in above method to
(UIButton *)
You can get the value that which button is being tapped by doing sender.tag.
I always use tags.
You need to subclass the
UITableviewCell
and handle the button press from there.