Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find indexPath for tapped button in tableView Using Seque

I have a UITableView using prototype cell.

The cells have a UIButton that show a UIViewController (Modal) using Segue.

My question is : How can i pass IndexPath of cell to second view controller

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    if ([segue.identifier isEqualToString:@"showGamerDetail"]) {

            //its not worked:
            NSIndexPath *indexPath = [self._tableView indexPathForSelectedRow];
            nDetailViewController *destinationViewController = segue.destinationViewController;
            destinationViewController.selectedRowTitle = [gamers objectAtIndex:indexPath.row];

        destinationViewController.indexPath = indexPath;
        destinationViewController.delegate = self;
    }
like image 778
nmokkary Avatar asked May 21 '14 13:05

nmokkary


2 Answers

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:

Cell by button position

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

Cell by while cycle on the button superviews

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

Cell by button tag

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

Cell by design pattern

// 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 ;)

like image 139
Matteo Gobbi Avatar answered Oct 19 '22 09:10

Matteo Gobbi


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

}
like image 43
Vins Avatar answered Oct 19 '22 10:10

Vins