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.
create an nsmutable array and put all button in that array usint[array addObject:yourButton];
in the button press method
-
With Swift 4.2 and iOS 12, you can choose one the following complete examples in order to solve your problem.
#1. Using
UIView
'sconvert(_:to:)
andUITableview
'sindexPathForRow(at:)
#2. Using
UIView
'sconvert(_:to:)
andUITableview
'sindexPathForRow(at:)
(alternative)This is an alternative to the previous example where we pass
nil
to thetarget
parameter inaddTarget(_:action:for:)
. This way, if the first responder does not implement the action, it will be send to the next responder in the responder chain until until a proper implementation is found.#3. Using
UITableview
'sindexPath(for:)
and delegate patternIn this example, we set the view controller as the delegate of the cell. When the cell's button is tapped, it triggers a call to the appropriate method of the delegate.
#4. Using
UITableview
'sindexPath(for:)
and a closure for delegationThis is an alternative to the previous example where we use a closure instead of a protocol-delegate declaration to handle the button tap.