Reputation: 1924
I want to place two buttons in each table view cell. When I click on button number one I want the app to show an alert message: "You tapped button1 at indexpath:3,0". My problem is: how can I place the buttons in a table view cell? Can anyone guide me?
Upvotes: 5
Views: 6412
Reputation: 40502
Taking @wedo's answer and simplifying it a bit -- you essentially need two pieces of information : the row and column number that was tapped ("column" being the order of the button).
This can be stored on a button using button.tag
and button.titleLabel.tag
. In -tableView:cellForRowAtIndexPath:
you would do this:
UIButton *button0 = [UIButton buttonWithType:UIButtonTypeCustom];
button0.tag = indexPath.row;
button0.titleLabel.tag = 0; // button #0 (or column 0)
[button0 addTarget:self action:@selector(cellButtonAction:)
forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button0];
Your cellButtonAction:
method would look like this:
- (IBAction)answerButtonAction:(UIButton *)sender {
NSInteger row = sender.tag;
NSInteger column = sender.titleLabel.tag;
// do something
}
The above works and it's fine, but it is rather hacky. Alternately, it is maybe 3 minutes work to subclass a button and add a property that can hold row and column values.
@interface IndexPathButton: UIButton
// NSIndexPath provides a convenient way to store an integer pair
// Note we are using cellIndex.section to store the column (or button #)
@property (strong, nonatomic) NSIndexPath *cellIndex;
@end
@implementation IndexPathButton
@end
You would use this in much the same way as the previous solution, but store the values in the custom property rather than tags. In tableView:cellForRowAtIndexPath:
// You'd create a button for each column here
IndexPathButton *button0 = [IndexPathButton buttonWithType:UIButtonTypeCustom];
button0.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
[button0 addTarget:self action:@selector(cellButtonAction:)
forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button0];
UITableViewCells
should generally use delegation for any heavy lifting that needs to be done. This pattern most closely matches Apples own delegate pattern for cells, e.g. tableView:didSelectRowAtIndexPath
and friends. So, let's create a tableViewCell base class that can be used to handle any number of controls and which doesn't need to pass around indexPaths.
/** Simple protocol to allow a cell to fire any type of action from a control. */
@protocol SOTableViewCellActionDelegate <NSObject>
@required
-(void)tableViewCell:(UITableViewCell *)cell didFireActionForSender:(id)sender;
@end
@interface SOActionCell : UITableViewCell
@property (nonatomic, weak) id<SOTableViewCellActionDelegate> delegate;
@end
@implementation SOActionCell
-(void)fireAction:(id)sender
{
[self.delegate tableViewCell:self didFireActionForSender:sender];
}
@end
In -tableView:cellForRowAtIndexPath:
you would do this:
UIButton *button0 = [UIButton buttonWithType:UIButtonTypeCustom];
button0.tag = 0;
[button0 addTarget:cell action:@selector(fireAction:)
forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button0];
Then implement the required delegate method in the tableViewController:
-(void)tableViewCell:(UITableViewCell *)cell didFireActionForSender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSAssert(indexPath, @"indexPath of cell shall always be found."];
if (!indexPath)
return;
// do whatever you want to do with your button action here
// using indexPath, sender tag, button title, etc.
}
Upvotes: 7
Reputation: 1119
Using indexPath as the tag value is OK when you have only one button in a UITableCell but if you want to track more you can use modulo operator:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
...
imageButton1.tag=indexPath.row*2;
[imageButton1 addTarget:self action:@selector(buttonPushed:)
[cell.contentView addSubview:imageButton];
...
imageButton2.tag=indexPath.row*2+1;
[imageButton2 addTarget:self action:@selector(buttonPushed:)
[cell.contentView addSubview:imageButton];
for the selector you can distinguish between the buttons and get the indexPath like this:
-(void) buttonPushed:(id)sender{
UIButton *button = (UIButton *)sender;
switch (button.tag%2) {
case 0: // imageButton1 is pressed
// to reach indexPath of the cell where the button exists you can use:
// ((button.tag-button.tag%2)/2)
break;
case 1: // imageButton2 is pressed
break;
}
The example is for 2 buttons but you can adjust it according to the number of buttons.
Upvotes: 5