I have a UITableView
using prototype cell.
The cells have a UIButton
that show a UIViewController
(Modal) using Segue
Very simple solution, if you want to point to the same segue of the selected cell.
- (IBAction)buttonPressed:(id)sender {
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
Forget for a moment that you are using storyboard, and let's try to follow the best approach.
You have several solution, but for my opinion, just 1 of these is optimal: delegation pattern
.
First, you should extend your cell, and using delegation to return the cell when the user press the button. Then, you should use indexPathForCell
to get the indexPath
.
Let's see the approaches:
- (void)buttonClicked:(id)sender
CGPoint buttonPosition = [sender convertPoint:CGPointZero
toView:self.tableView];
NSIndexPath *tappedIP = [self.tableView indexPathForRowAtPoint:buttonPosition];
// When necessary
// UITableViewCell *clickedCell = [self.tableView cellForRowAtIndexPath:tappedIP];
}
the solution above is certainly the most rapid to implement, but it is not the best from the point of view of the design/architecture. Moreover you get the indexPath
but you need then to calculate every other info. This is a cool method, but would say not the best.
// ContactListViewController.m
- (IBAction)emailContact:(id)sender {
YMContact *contact = [self contactFromContactButton:sender];
// present composer with `contact`...
}
- (YMContact *)contactFromContactButton:(UIView *)contactButton {
UIView *aSuperview = [contactButton superview];
while (![aSuperview isKindOfClass:[UITableViewCell class]]) {
aSuperview = [aSuperview superview];
}
YMContactCell *cell = (id) aSuperview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
return [[self fetchedResultsController] objectAtIndexPath:indexPath];
}
Get the cell in this way is less performant of the former, and it is not elegant as well.
- (CustomCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.theLabel.text = self.theData[indexPath.row];
cell.button.tag = indexPath.row;
[cell.button addTarget:self action:@selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)doSomething:(UIButton *) sender {
NSLog(@"%@",self.theData[sender.tag]);
//sender.tag will be equal to indexPath.row
}
Absolutely no. Use the tag could seem a cool solution, but the tag of a control can be used for other reasons, like the next responder etc. I don't like this approach.
// YMContactCell.h
@protocol YMContactCellDelegate
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
@end
@interface YMContactCell
@property (weak, nonatomic) id<YMContactCellDelegate> delegate;
@end
// YMContactCell.m
- (IBAction)emailContact:(id)sender {
[self.delegate contactCellEmailWasTapped:self];
}
// ContactListViewController.m
- (void)contactCellEmailWasTapped:(YMContactCell*)cell;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
YMContact *contact = [[self fetchedResultsController] objectAtIndexPath:indexPath];
// present composer with `contact` ...
}
This is my favourite solution.
Using delegation
or blocks
is a really nice approach and you can pass all the parameters you want. In fact you could want to send back directly the needed info, without having to calculate them later.
Enjoy ;)
First make a global int variable selecteIndex
;
In your cell, give your button tag similar to cell's indexPath.row
value
E.g. btn.tag = indexPath.row;
The assing & call action on click of that button
[btn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
-(void)buttonClicked:(id)sender
{
UIButton *btn = (UIButton*)sender;
selectedIndex = btn.tag;
[self performSegueWithIdentifier:@"segueName" sender:self];
}
Then at end of your view write this function
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"segueName"]) {
viewController *dvc = (viewController *)[segue destinationViewController];
[dvc setSelected:selectedIndex];
};
}