Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two buttons in a UITableViewCell: How to tell which indexPath was selected?

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?

like image 341
Prasad Avatar asked Jan 18 '11 15:01

Prasad


1 Answers

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).


Solution 1 - Not a Bad Solution

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
}

Solution 2 - A Much Better Solution

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]; 

Solution 3 - The Best Solution

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.
}
like image 169
memmons Avatar answered Oct 15 '22 18:10

memmons