What Are Unwind Segues For and How to Use Them

What are Unwind segues for and how do you use them?

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:

Sample Image

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:

Sample Image

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

Sample Image

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:

Sample Image

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:

Sample Image

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

Sample Image

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)

how do you use unwind segue when you have multiple ViewController that calls it

As you have discovered, you can implement the unwind target @IBAction in multiple viewControllers and iOS will go up the call chain until it finds one. This allows viewController E to return to whomever called it as long as they implement the target function unwindFromE.

In the case of your E unwind returning to C when the call stack was F->X->E, this happens when C and F are viewControllers controlled by the same UITabBarController. Since F doesn't implement unwindFromE, iOS searches the other viewControllers controlled by the UITabBarController. In your case, it found C and switched to that tab.

More information about unwind segues can be found in Technical Note TN2298: Using Unwind Segues.

How do unwind segues differentiate between targets with same name in different classes?

Unwind segues involve a run-time process that navigates from the current view controller, back up the chain of preceding view controllers, looking for the first instance it encounters that has an unwind action of the appropriate name. This means that it unwinds to the most recent occurrence.

So, if you go from view controller A to B to C, and A and B have an unwind segue of the same name, when you unwind from C using the unwind segue of that name, it will unwind to B, the most recent one, not A.

The run-time process of walking up the chain of view controllers is outlined in WWDC 2012 video Adopting Storyboards in Your App.


The above begs the question of the best choice for unwind action names. It's easy to jump to the conclusion of "just use unique unwind action names", but I think it's more complicated than that. Whether you use unique unwind action names or common/shared action names is just a question of the intent of the unwind action.

For example, sometimes you want to unwind all the way back to a particular scene. In that case you'd use some unique unwind action name. So, in our A » B » C example, A might implement unwindToA and B might implement unwindToB.

But consider some flow by which the user could possibly navigate from A » B » D or alternatively A » C » D and you want D to unwind to either B or C, as appropriate. In that case, B and C might implement a unwindFromD action.

I suspect it was the desire for this sort of flexibility that prompted Apple to implement this sort of "walk up the chain of view controllers" run-time process rather than a "look for view controller of particular class" sort of approach.

Regardless, the choice of unique vs shared unwind action names simply depends your particular use-case.

Why do we use unwind segue if we can use segues to go back to any view controller?

Unwind segues give you a way to "unwind" the navigation stack back through push, modal, popover, and other types of segues. You use unwind segues to "go back" one or more steps in your navigation hierarchy. Unlike a normal segue, which create a new instance of their destination view controller and transitions to it, an unwind segue transitions to an existing view controller in your navigation hierarchy. Callbacks are provided to both the source and destination view controller before the transition begins. You can use these callbacks to pass data between the view controllers.

When you create an unwind segue in Interface Builder you do not specify a destination view controller. The unwind segue determines which view controller in your navigation hierarchy to unwind to when it is triggered at runtime. This process is customizable and additional points of customization are provided for container view controllers to participate in the unwind process.

Check : https://developer.apple.com/library/ios/technotes/tn2298/_index.html

 -(IBAction)unwindForSegue:(UIStoryboardSegue *)unwindSegue towardsViewController:(UIViewController *)subsequentVC{


}

How to get unwind segue to work between along side a regular segue in the same view controller?

This function in MoonViewController:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let des = segue.destination as! JupiterViewController //<- ERROR HERE

if (segue.identifier == "toJupiterView")
{
print("Went to Jupiter")
}
}

is called both for regular segues and unwind segues. When you are segueing to JupiterViewController then segue.destination is indeed of type JupiterViewController. But when you are unwinding to ViewController, segue.destination is of type ViewController.

In this second case, the force cast as! JupiterViewController crashes because the type is wrong.

One way to fix that is to move the declaration of des inside of the if when you have identified the destination:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toJupiterView"
{
let des = segue.destination as! JupiterViewController
print("Went to Jupiter")
}
}

how to unwind segue to the view controller that called it

thanks to @luk2302 I was able to figure it out. No need to even use unwind segue. Thanks luk2302!

@IBAction func returnViewController(sender: AnyObject) {
if((self.presentingViewController) != nil){
self.dismissViewControllerAnimated(true, completion: nil)
}
}

Is it possible to use an unwind segue from a single modal view controller back to one of multiple instances of the same source view controller?

Apple has a comprehensive technical note on how unwind segues work and how the destination view controller is determined, but, in summary, the process examines the view controller navigation hierarchy to find the first view controller that can handle the unwind segue and is willing to do so.

In your case, this would be the MainVC instance that presented the ModalVC that is unwinding. The unwind segue cannot be handled by a view controller instance that is not in the navigation hierarchy (e.g. An instance of MainVC that did not present the ModalVC)



Related Topics



Leave a reply



Submit