Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView prepareForSegue assigning indexPath to sender?

In the code below I am just trying out setting up a segue directly from the UIViewController to a DetailViewController. I usually do the segue from the UITableViewCell (which I think is clearer) but this time I just wanted to try going directly from the ViewController using -tableView:didSelectRowAtIndexPath:

My question is: I got to a situation where I needed to access the UITableViewCell indexPath from within -prepareForSegue:sender: my solution was to send the indexPath via sender. Usually I would set sender to self here, I have looked around and I can't find any reference to this, I am just curious if using sender to pass an indexPath is acceptable? I am pretty sure it is, and I am sure I have read that you can somewhere before, but I can't find any reference to it now.

// ------------------------------------------------------------------- **
// DELEGATE: UITableView
// ------------------------------------------------------------------- **
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [self performSegueWithIdentifier:@"SEGUE_TWO" sender:indexPath];
}

// ------------------------------------------------------------------- **
// SEGUE: ViewController_ONE > DetailViewController
// ------------------------------------------------------------------- **
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    if([[segue identifier] isEqualToString:@"SEGUE_TWO"]) {
        NSUInteger cellRow = [(NSIndexPath *)sender row];
        DetailViewController *destinationController = [segue destinationViewController];
        [destinationController setDataString:[[self dataList] objectAtIndex:cellRow]];
    }
}

EDIT: What I was looking for in this case was

NSIndexPath *indexPath = [[self tableView] indexPathForSelectedRow]; 

essentially a way to get indexPath into -performForSegue:sender: without having to pass it via the sender parameter (in -performSegueWithIdentifier:sender:)

like image 984
fuzzygoat Avatar asked Mar 14 '13 15:03

fuzzygoat


1 Answers

I typically avoid using the sender in prepareForSegue: because it could be anything. This makes asserting that your sender is what you are expecting more cumbersome and fragile. Also, I prefer to check for the class of the destinationViewController object rather than the segue name. Your functionality is tied to your object (or more specficially its interface), not the title of your segue, so checking your class makes it clear what you are trying to accomplish.

Below is the usual template I use in production when segueing from a UITableView. Note that you should always check if the destinationViewController is embedded in a nav controller and then check the class of the object as well to make sure you are handling it correctly. Finally, use an assert to ensure that somewhere down the line you haven't added some new segue that you forgot to handle.

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // unwrap the controller if it's embedded in the nav controller.
    UIViewController *controller;
    UIViewController *destVC = segue.destinationViewController;
    if ([destVC isKindOfClass:[UINavigationController class]]) 
    {
        UINavigationController *navController = (UINavigationController *)destVC;
        controller = [navController.viewControllers firstObject];
    } 
    else 
    {
        controller = destVC;
    }

    if ([controller isKindOfClass:[DetailViewController class]]) 
    {
        DetailViewController *vc = (DetailViewController *)controller;
        NSIndexPath *ip = [self.tableView indexPathForSelectedRow];
        [vc setDataString:[[self dataList] objectAtIndex:ip.row]];

    } 
    else 
    {
        NSAssert(NO, @"Unknown segue. All segues must be handled.");
    }

}
like image 65
memmons Avatar answered Nov 11 '22 02:11

memmons