0
votes

Unwind segues seem not to behave as expected in iOS 8.1 when combined with a modal view and container view. Here's the view controller hierarchy for the test project which can be found on github:

Unwind segue bug hierarchy image

Tapping on the "tap me" button pushes the modal view which is embedded in a navigation controller and which has a tableView as a child view controller. Tapping on a row in the tableView pushes another tableView. Finally, tapping on a row in this final tableView should call the unwind segue named bUnwindSegue found on the previous view controller.

Problems:

  1. bUnwindSegue is never called.
  2. According to technical note TN2298 a container view controller is responsible for selecting the child view controller to handle a segue. In this case viewControllerForUnwindSegueAction:fromViewController:withSender: should be called on the container view controller. It isn't.

In the example project, you can see that BTableViewController contains the unwind segue:

- (IBAction)bUnwindSegue:(UIStoryboardSegue *)segue;
{
    NSLog(@"Unwinding...this unwind segue will never get called.");
}

In the storyboard, the cell selection action for CTableViewController is indeed the bUnwindSegue. Also note that if you change the cell select action of CTableViewController to the unwind segue in the container view controller -- containerVCUnwindSegue -- that the segue is called correctly.

Are unwind segues not behaving as expected?

1
Your unwind segue method should be in the view controller you're unwinding to. - Lyndsey Scott
@LyndseyScott BTableViewController is the VC that is being unwound to. - memmons
@LyndseyScott The unwind segue is currently set via storyboard. Changes would need to happen there as well. Alternately, you could remove the storyboard unwind and set it programmatically. I would expect both cases to behave the same. - memmons
Just realized I misread that second sentence I referenced... And I was generally confusing B C and TableViewControllers... I'm caught up now though and thinking it over.. - Lyndsey Scott
The reason it's not transitioning back unless you put the segue in the container view controller is because unlike CTableViewController, BTableViewController hasn't been added to the stack as a standalone view controller, but as a container of your container view controller. - Lyndsey Scott

1 Answers

0
votes

(1) You're misunderstanding the technical note TN2298 you cited and (2) you're not overriding viewControllerForUnwindSegueAction: appropriately.

As the TN2298 doc section you linked to about Container View Controller states underneath its "Selecting a Child View Controller to Handle An Unwind Action" subheading:

Your container view controller should override the method shown in [viewControllerForUnwindSegueAction:] to search its child view controllers for a view controller that wants to handle the unwind action. If none of a container's child view controllers want to handle the unwind action, it should invoke the super's implementation and return the result.

First off, to override the method, you have to subclass the UINavigationController in your storyboard and add the viewControllerForUnwindSegueAction: method there. After doing that, you'll see the method is now being called as expected.

But your second error is that your current attempt to override the viewControllerForUnwindSegueAction: method simply contains return self;. You should instead be returning the view controller that you'd like to handle the unwind action.

So say, for example, you have a public variable in VCWithContainedVCsViewController to hold the current instance of BTableViewController and you access you're able to access that current container view controller, ex:

- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
    NSLog(@"Technical note TN2298 indicates child VCs defer to their parent to determine where an unwind segue will be handled.");

    if ([NSStringFromSelector(action) isEqualToString:@"bUnwindSegue:"]) {

        NSLog(@"%@", self.viewControllers);
        VCWithContainedVCsViewController *containerVC = (VCWithContainedVCsViewController*)self.viewControllers[0];
        return containerVC.container;
    }

    return [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}

What you'll see in that case is that bUnwindSegue: is in fact being called (your message should print), but the segue still won't happen.

Why is this?

Because as I mentioned in the comments, BTableViewController is not on the current navigation stack. Some child view controllers of BTableViewController, like CTableViewController, will be on the navigation stack because, for example, CTableViewController is not a container view. But BTableViewController itself is not capable of performing the segue on its own because it is not on the current navigation stack. So although you can in fact select some child view controllers to handle unwind actions as the documentation states, BTableViewController isn't going to be one of them.