Although a segue can "create" a new instance of a view controller, it cannot "decreate" an earlier view controller. A segue cannot be reversed once it has been initiated. Like a one-way street, it is.
If you have View Controllers A, B, and C, that is. You transition from A to B and from B to C, but you wish to go back to A instead of B after dismissing C. The back button on a navigation controller is useless in this situation because it always leads to B.
Unwind segues provide an intuitive solution to these issues, which is why I believe they are an important but underutilised feature in your toolbox as an iOS developer.
How does an unwind segue work? What is it?
By removing all view controllers before the destination, an unwind segue enables you to jump to any view controller higher up in your view controller hierarchy.
Let's take a quick look at how an unwind segue functions before beginning to create one.
- The destination view controller includes an unwind method. The runtime is informed by this method's only parameter, a UIStoryboardSegue, that this view controller can be the target of an unwind segue.
- The storyboard's unwind segue creation.
- The source view controller is where you start the unwind segue.
- The shouldPerformSegue(withIdentifier:sender:) method of the source view controller is used.
- In order to find the desired view controller, the runtime searches through the hierarchy of view controllers. The unwind function will be used by this view controller, which it discovers first.
- As with a typical segue, the source's prepare(for:sender:) method is invoked, providing the ideal chance to pass data from the source to the destination.
- Our segue is passed as the input argument to the unwind method in the destination view controller. This approach is frequently ineffective and just acts as a marker.
- The transition is made.
Implementation
It's time to use our first unwind segue at this point. Here, I'm presuming that you are familiar with the basics of view controller creation.
Set up the project
Make a new Xcode project, set the language to Swift, make it a single view app, and choose the Storyboard as the User Interface.
When your project is finished, make the following adjustments:
- In the Storyboard, add three new view controllers. I'll call them View Controller A, B, and C.
- Include them in a navigation controller by selecting "Editor" > "Embed In"
- Construct a show segue from View Controller A to View Controller B and from B to C. Assign each a special identification.
- Each view controller should be given a button. This will start our show segue in A and B, and start our unwind segue in C.
When you're finished, your storyboard should resemble the following:
Then, when the buttons are pushed, cause the show transitions in view controllers A and B:
Create the unwind segue
Implementing the unwind technique comes first. This must be done in your final view controller. That is ViewControllerA for us.
@IBAction func unwind( _ seg: UIstoryboardSegue) {
}
The UIStoryboardSegue that we will trigger must be passed in as a single parameter and marked as an IBAction. Although I gave it the name "unwind," you can give it whatever name you wish.
As was already noted, this method is typically left empty. It is called when the unwind segue is triggered, but its runtime signalling capabilities are far more crucial.
Returning to the Storyboard, we now choose ViewControllerC and drag it from the view controller to the Exit proxy.
We choose our unwinding strategy in the popup.
Finally, we designate a name for our unwind segue.
@IBAction func backToATapped(_ sender: Any) {
preformSegue(withIdentifier: "unwindToA", sender:self)
}
All done!
Some technical details
I want to look at what the runtime is doing internally before I wrap up this post.
It basically involves walking up the view controller hierarchy to find the destination. The parent or presentingViewController of the current view controller will be the following one.
The unwind method might be contained in one of the view controller's children, therefore the runtime must also determine if there are any at each stop
The allowedChildrenForUnwinding(from:) method, which returns an array of UIViewControllers that are the view controller's children, is called in this situation.
Since we just left that branch, we don't want to check the branch that contains the source view controller. Calling the childContaining(_:) method, which returns the child view controller that contains the, prevents it from happening.