6

Simple storyboard setup: UIViewController with UINavigationController. On a table cell click a custom segue pushes a new UIViewController onto the navigation stack. All good. But then pressing the "back" button in the navigation bar, it only uses the default pop animation.

How do I tell the navigation controller to use my custom segue when popping back? I've added

- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier

- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender

to both view controllers but they don't get called at all.

What am I missing?

tcurdt
  • 14,518
  • 10
  • 57
  • 72

3 Answers3

11

You can accomplish this by creating a custom transition instead of using a custom segue. It took me a while to understand, but it is basically very simple. Just make sure that your first view controller adopts the UINavigationControllerDelegate protocol. Then, still in the first view controller, implement the following method:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                              animationControllerForOperation:(UINavigationControllerOperation)operation
                                           fromViewController:(UIViewController *)fromVC
                                             toViewController:(UIViewController *)toVC
{
return X // where X is an animation Controller object, an instance of the animator class.  
}

Still in the first view controller, for example in ViewDidLoad, tell the navigationcontroller that the first view controller will act as its delegate:

self.navigationController.delegate = self;

Last but not least, create an animator class that adopts the UIViewControllerAnimatedTransitioning protocol. Implement the following methods:

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

and

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

The first method holds the transition code. This transition code will automatically be used when tapped on the back button.

Here is a very simple example project.

Tuslareb
  • 1,200
  • 14
  • 21
  • I was trying to crack Custom animation using storyboard for days ! Thanks ! your example project helped a lot . UpVoted ! :) – NavinDev Aug 19 '14 at 11:27
  • I added the navigationcontroller delegate method to the navigationcontroller itself and set its delegate to itself and it works, so you dont have to do that process and use that protocol in every vc in the stack. – Arbitur Feb 15 '15 at 10:29
  • @Arbitur I assume this navigationcontroller of yours is subclassed and is inherited across all view controllers in the stack, right? if not, how are you doing what you describe? – Pavan Mar 11 '15 at 17:25
  • @Pavan Yes, I subclassed it, less than 5 line in Swift. – Arbitur Mar 11 '15 at 17:29
  • Thanks for this post! It stopped me from going down the custom segue path. I also might add that I needed a custom transition for just one segue. What I did was set and unset (set to nil) the `navigationController.delegate` in the `prepareForSegue` method. – tonchis Jun 24 '15 at 16:52
2

Have you tried using something like:

- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
{
    return [MyCustomSegueInitMethod viewControllerA:vc1 viewControllerB: vcB];
}

I've never made a custom segue, but assuming they work like all others do when overriding them, as long as your custom segue class is subclassing the UIStoryBoardSegue, it should work...

Then make sure you've linked your back button to the "exit" icon in the bar underneath the view controller.

jpcguy89
  • 217
  • 2
  • 16
  • As stated above I have tried this and the selector is not getting called. Also there is no back button as that is coming from the navigation controller. – tcurdt Mar 20 '13 at 17:21
  • 1
    And that's your problem... You need create a custom UIToolbar with your own UIButton "back" button and link it to the exit icon, and call the unwind() method via a connected touchUpInside event you create for the back button. The problem is, is that you cannot "unwind()" if you use a navigation controller's back method. You have to hide the UINavigation bar and implement your own solution. – jpcguy89 Mar 20 '13 at 17:22
  • @tcurdt Sorry, from above, it's a UIToolbar with a UIBarButtonItem, not a UIButton – jpcguy89 Mar 20 '13 at 17:28
  • Then I don't get what `segueForUnwindingToViewController` is for. Faking a UINavigationBar is not really an option. – tcurdt Mar 20 '13 at 17:50
  • segueForUnwindingToViewController... I think this method is usually used in the context of Container View Controllers. That's why it doesn't work with UINavigationViewController. – Burny Sep 09 '13 at 18:05
2

Unwind segues are useful to “roll back” segues, including custom segues. The problem in your usecase does not lie with segues per se, but with the fact that you want to trigger the unwind segue from the back button.

This said, you could try to work around this by not using unwinding, but trigger a custom segue when the standard-back-button is tapped:

// MyDetailViewController.m:
- (void)viewWillDisappear:(BOOL)animated
{
    if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
        // trigger custom segue (but note that the sourceViewController
        // has already been removed from the nav controller)
    }
    [super viewWillDisappear:animated];
}

Obviously this no longer has anything to do with unwinding - it’s just performing a custom segue that you implement to “roll back” the segue you used to “push” the detail view controller in the first place.

Let me know how this goes.

Yang Meyer
  • 5,409
  • 5
  • 39
  • 51
  • Triggering a custom segue is not rolling back a segue. Check the little information that is out there. You have to create some weird IBAction connection so a roll back works. – tcurdt May 23 '13 at 10:06
  • Yes, you will need to (1) implement a method with this signature `-(IBAction)myUnwindMethod:(UIStoryboardSegue*)segue` in your parent view controller, (2) connect some control in the child view controller to the green Exit icon in (or manually call this method from -viewWillDisappear: in your case, because you want to "hijack" the navigation controller's back button), and (3) implement the two methods you mention in your question. – Yang Meyer May 24 '13 at 10:10