593

iOS 6 and Xcode 4.5 has a new feature referred to as "Unwind Segue":

Unwind segues can allow transitioning to existing instances of scenes in a storyboard

In addition to this brief entry in Xcode 4.5's release notes, UIViewController now seem to have a couple of new methods:

- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier

How do unwind segues work and what they can be used for?

max_
  • 22,619
  • 38
  • 116
  • 207
Imre Kelényi
  • 21,863
  • 5
  • 31
  • 44

6 Answers6

1276

In a Nutshell

An unwind segue (sometimes called exit segue) can be used to navigate back through push, modal or popover segues (as if you popped the navigation item from the navigation bar, closed the popover or dismissed the modally presented view controller). On top of that you can actually unwind through not only one but a series of push/modal/popover segues, e.g. "go back" multiple steps in your navigation hierarchy with a single unwind action.

When you perform an unwind segue, you need to specify an action, which is an action method of the view controller you want to unwind to.

Objective-C:

- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue
{
}

Swift:

@IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}

The name of this action method is used when you create the unwind segue in the storyboard. Furthermore, this method is called just before the unwind segue is performed. You can get the source view controller from the passed UIStoryboardSegue parameter to interact with the view controller that initiated the segue (e.g. to get the property values of a modal view controller). In this respect, the method has a similar function as the prepareForSegue: method of UIViewController.

iOS 8 update: Unwind segues also work with iOS 8's adaptive segues, such as Show and Show Detail.

An Example

Let us have a storyboard with a navigation controller and three child view controllers:

enter image description here

From Green View Controller you can unwind (navigate back) to Red View Controller. From Blue you can unwind to Green or to Red via Green. To enable unwinding you must add the special action methods to Red and Green, e.g. here is the action method in Red:

Objective-C:

@implementation RedViewController

- (IBAction)unwindToRed:(UIStoryboardSegue *)unwindSegue
{
}

@end

Swift:

@IBAction func unwindToRed(segue: UIStoryboardSegue) {
}

After the action method has been added, you can define the unwind segue in the storyboard by control-dragging to the Exit icon. Here we want to unwind to Red from Green when the button is pressed:

enter image description here

You must select the action which is defined in the view controller you want to unwind to:

enter image description here

You can also unwind to Red from Blue (which is "two steps away" in the navigation stack). The key is selecting the correct unwind action.

Before the the unwind segue is performed, the action method is called. In the example I defined an unwind segue to Red from both Green and Blue. We can access the source of the unwind in the action method via the UIStoryboardSegue parameter:

Objective-C:

- (IBAction)unwindToRed:(UIStoryboardSegue *)unwindSegue
{
    UIViewController* sourceViewController = unwindSegue.sourceViewController;

    if ([sourceViewController isKindOfClass:[BlueViewController class]])
    {
        NSLog(@"Coming from BLUE!");
    }
    else if ([sourceViewController isKindOfClass:[GreenViewController class]])
    {
        NSLog(@"Coming from GREEN!");
    }
}

Swift:

@IBAction func unwindToRed(unwindSegue: UIStoryboardSegue) {
    if let blueViewController = unwindSegue.sourceViewController as? BlueViewController {
        println("Coming from BLUE")
    }
    else if let redViewController = unwindSegue.sourceViewController as? RedViewController {
        println("Coming from RED")
    }
}

Unwinding also works through a combination of push/modal segues. E.g. if I added another Yellow view controller with a modal segue, we could unwind from Yellow all the way back to Red in a single step:

enter image description here

Unwinding from Code

When you define an unwind segue by control-dragging something to the Exit symbol of a view controller, a new segue appears in the Document Outline:

enter image description here

Selecting the segue and going to the Attributes Inspector reveals the "Identifier" property. Use this to give a unique identifier to your segue:

enter image description here

After this, the unwind segue can be performed from code just like any other segue:

Objective-C:

[self performSegueWithIdentifier:@"UnwindToRedSegueID" sender:self];

Swift:

performSegueWithIdentifier("UnwindToRedSegueID", sender: self)
Imre Kelényi
  • 21,863
  • 5
  • 31
  • 44
  • 12
    +1 great answer. They sound really nice, but can't methods like `dismissViewControllerAnimated:completion:` or `popViewControllerAnimated:` achieve the same thing? – Sam Spencer Jul 02 '13 at 16:52
  • 33
    Sure they can. However, if you are using storyboards, unwind segues can often achieve the same thing with much less code. Actually, now you can dismiss a modally presented view controller without writing any code. Of course, there are still many cases when closing controllers from code is the right thing to do. – Imre Kelényi Jul 05 '13 at 09:53
  • 7
    Make sure to add your action method to your header file or else Storyboard will not know about. – Kyle C Aug 28 '13 at 18:42
  • 18
    Another advantage over `dismissViewControllerAnimated:completion:` or `popViewControllerAnimated:` is that the method you added to the view controller you are unwinding to is called and so you have an easy way to know the presented view controller is finished without having to make the presenting view controller a delegate of the presented view controller. – honus Sep 24 '13 at 06:19
  • 1
    Can you skip view controllers and then unwind? Say if you segue to Yellow from Red, can you then unwind back to Green? – ecirish Dec 05 '13 at 23:24
  • 8
    Could I suggest a slight edit? It wasn't "obviously" clear that you put - (IBAction)unwindTRed:(UIStoryboardSegue *)unwindSegue in the RedViewController.m, and in turn this is universally available in "any" of the green exit buttons for any story board. Fantastic answer and now I will use this for other issues. Thanks! – John Ballinger Jan 22 '14 at 21:36
  • 4
    Another advantage for unwinding, no one will be able to follow the code so it will remain unchanged forever :) – Robert Apr 03 '14 at 13:52
  • You can leave the `unwindToRed` method empty and it will auto close the modal. – jmoz May 28 '14 at 18:28
  • 1
    You don't need to add the IBAction to the header file for interface builder to find it – Dustin Aug 06 '14 at 22:20
  • Note: If you cannot set the exit segues in the storyboard, you may need to restart your computer! Last time I had this issue, I tried restarting Xcode and it wasn't enough. – Senseful Dec 30 '14 at 21:18
  • I cannot set the exit segues in storyboard too, seems like a bug in xcode. Restarting the computer didn't do it, anyone has another solution? Thanks! – Van Du Tran Jan 27 '15 at 21:57
  • How do I push another view controller after it unwinds ? When I place a 'self.navigationController push' in the unwind action it pushes the new controller and then pops back to the unwind controller – ArdenDev Mar 11 '15 at 03:06
  • there is no way to do a segue and unwind segue without Storyborad? or can you share a solution to create a segue with pure Objective-C Code? – Huang Minghe Mar 25 '15 at 02:03
  • How does one add the exit segue to the appropriate controller when you can only see the "Exit" object for the currently selected view controller? I added it to the destination view controller but am getting "Receiver … has no segue with identifier 'applyDateSegue'" when I attempt to perform it from the child. – devios1 Apr 07 '15 at 23:05
  • 1
    Nevermind! It turns out I don't actually need to connect the segue to the destination controller at all, but rather pick the action from the list, which magically includes *all* actions from all views in the storyboard. – devios1 Apr 07 '15 at 23:07
  • @IphoneDeveloper - That's because the unwind action is called before the view has been popped. Instead, you should place the `self.navigationController push` in the `viewDidAppear` function instead, which will push a view controller just after the current has appeared. You will also need to use a `Bool` to decide if you want to push. – EmirC Apr 20 '15 at 13:29
  • Can we use 2 unwind segues? i.e. if I have RedVC>GreenVC>BlueVC GreenVC>YellowVC>BlackVC>GrayVC now If I want to unwind from BlueVC to RedVC then I need to create “unwindtoRedVC" and If I want to unwind from GrayVC to GreenVC thenI need to create “unwindToGreenVC”. So can I create both? Will it create any issues? – arjavlad May 06 '15 at 05:42
  • 1
    Great! Also works for manual segues (i.e., instead of dragging from a button in the source vc's view, drag from the vc itself and perform the segue programmatically with `performSegueWithIdentifier:sender:` ) – Nicolas Miari Jul 20 '15 at 06:36
  • What happened to segue chain when going to another App? Because when I return I can not unwind back! – new2ios Oct 28 '15 at 10:00
  • Great answer but I will extract the most important thing to me: It works with push/modal/popover and doesn't work for switch ! I spent a day searching for this one! Hope helps others as well – Mo Zaatar Sep 07 '17 at 05:25
  • Unfortunately the described way will produce strong reference cycle (the button dragged to the controller) – Ivaylo Nikolov Sep 26 '17 at 12:47
168

As far as how to use unwind segues in StoryBoard...

Step 1)

Go to the code for the view controller that you wish to unwind to and add this:

Objective-C

- (IBAction)unwindToViewControllerNameHere:(UIStoryboardSegue *)segue {
    //nothing goes here
}

Be sure to also declare this method in your .h file in Obj-C

Swift

@IBAction func unwindToViewControllerNameHere(segue: UIStoryboardSegue) {
    //nothing goes here
}

Step 2)

In storyboard, go to the view that you want to unwind from and simply drag a segue from your button or whatever up to the little orange "EXIT" icon at the top right of your source view.

enter image description here

There should now be an option to connect to "- unwindToViewControllerNameHere"

That's it, your segue will unwind when your button is tapped.

Dmitry
  • 13,126
  • 20
  • 95
  • 177
Travis M.
  • 10,360
  • 1
  • 50
  • 70
  • 5
    I have found that with Xcode 4.5 and earlier it was necessary to declare the IBAction in the header. I don't know if this is still true. – Steven Fisher Jun 21 '13 at 19:53
  • 2
    Is there a way to do Step 2 without storyboard, i.e. programmatically? My storyboard (interface builder) is messed up and it doesn't show the unwind segues (xcode bug). – Van Du Tran Jan 27 '15 at 21:55
46

Unwind segues are used to "go back" to some view controller from which, through a number of segues, you got to the "current" view controller.

Imagine you have something a MyNavController with A as its root view controller. Now you use a push segue to B. Now the navigation controller has A and B in its viewControllers array, and B is visible. Now you present C modally.

With unwind segues, you could now unwind "back" from C to B (i.e. dismissing the modally presented view controller), basically "undoing" the modal segue. You could even unwind all the way back to the root view controller A, undoing both the modal segue and the push segue.

Unwind segues make it easy to backtrack. For example, before iOS 6, the best practice for dismissing presented view controllers was to set the presenting view controller as the presented view controller’s delegate, then call your custom delegate method, which then dismisses the presentedViewController. Sound cumbersome and complicated? It was. That’s why unwind segues are nice.

Dmitry Minkovsky
  • 30,036
  • 20
  • 97
  • 138
Yang Meyer
  • 4,909
  • 4
  • 37
  • 51
  • 7
    You can call `dismissViewController:animated` from the presented controller. You don't have to delegate that. Of course if you need to pass data back, then you need delegation or some other method. – mxcl Sep 26 '13 at 12:01
  • 3
    While you can call `dismissViewController:animated:` from the presented controller, "best practice" was indeed to call a delegate method on the presenting controller to do that for you, as Yang mentioned. – Chris Nolet Jul 09 '14 at 03:05
37

Something that I didn't see mentioned in the other answers here is how you deal with unwinding when you don't know where the initial segue originated, which to me is an even more important use case. For example, say you have a help view controller (H) that you display modally from two different view controllers (A and B):

AH
BH

How do you set up the unwind segue so that you go back to the correct view controller? The answer is that you declare an unwind action in A and B with the same name, e.g.:

// put in AViewController.swift and BViewController.swift
@IBAction func unwindFromHelp(sender: UIStoryboardSegue) {
    // empty
}

This way, the unwind will find whichever view controller (A or B) initiated the segue and go back to it.

In other words, think of the unwind action as describing where the segue is coming from, rather than where it is going to.

shawkinaw
  • 3,151
  • 2
  • 25
  • 30
24

Swift iOS:

Step 1: define this method into your MASTER controller view. in which you want to go back:

//pragma mark - Unwind Seques
@IBAction func goToSideMenu(segue: UIStoryboardSegue) {

    println("Called goToSideMenu: unwind action")

}

Step 2: (StoryBoard) Right click on you SLAVE/CHILD EXIT button and Select "goToSideMenu" As action to Connect you Button on which you will click to return back to you MASTER controller view:

enter image description here step 3: Build and Run ...

Vinod Joshi
  • 7,094
  • 46
  • 49
1

For example if you navigate from viewControllerB to viewControllerA then in your viewControllerA below delegate will call and data will share.

@IBAction func unWindSeague (_ sender : UIStoryboardSegue) {
        if sender.source is ViewControllerB  {
            if let _ = sender.source as? ViewControllerB {
                self.textLabel.text = "Came from B = B->A , B exited"
            }
            
        }

}
  • Unwind Seague Source View Controller ( You Need to connect Exit Button to VC’s exit icon and connect it to unwindseague:

enter image description here

  • Unwind Seague Completed -> TextLabel of viewControllerA is Changed.

enter image description here

Tech
  • 544
  • 4
  • 17