Unwinding segues to communicate between two view controllers in the same view
After building a UITableView for my app’s Trip Log, I made some changes and realized that I wanted to place this table view as a subview inside of another VC. This would let users log a trip (add a row) to the table below, and display stats on the number of trips.
I wanted it to look like this:
I already had a UITableViewController for my table and a UIViewController for the + Button and stats labels. I found that a really easy way to hook up an existing table VC as a subview is by adding a container view to my main view and then ctrl dragging to my existing table view and selecting “embed”.
Here’s what that looked like in my storyboard:
Now my table view was displaying in my main view, and I could click the + button which incremented my stats and added the appropriate row to my table.
However, when I deleted a row from my table the stats did not decrement because there was nothing telling that view to refresh the stats. I needed to cal that method in my main view controller, but the problem was that I was still in my table view controller.
I tried using a delegate to make this happen, but I was having a hard time setting the delegate to the existing instance of my main view controller. So far when I have used delegates, the delegate gets set when a new instance of the VC is created. But in this case I wanted to return to the existing instance. I tried various ways of accessing that view controller instance but nothing was working.
I started reading more about embedded segues and I came across the phrase “unwind segue”. This is how Apple describes unwind segue:
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.
Sounded like my jam. I followed the steps in the documentation, but something was still not working. The documentation would sometimes refer to the source controller and the destination controller which made sense, but then it would start calling one of them the scene controller and I found it difficult to understand which controller it was referring to.
Here are the steps in how I finally got it to work:
1. In your destination controller you have to define an unwind action. The method doesn’t have to do anything to work, but in my case I wanted to call two methods in it which would update my stats. Here’s what I added:
@IBAction func unwindToStats(sender: UIStoryboardSegue) { fetchNumberTrips() fetchTripsInThirtyDayPeriod() }
2. In your source controller (in my case my table view controller, or the controller that the embed segue is going TO), ctrl drag from the controller icon to the exit icon and a popup should appear showing you the name of the action you just defined in your destination controller. Select that action so you’re wired up.
3. Give your unwind segue an identifier, I called mine “Unwind Segue”
4. In your source controller, define a function to call your segue using performSegueWithIdentifer. Then call that function in your source controller’s code whenever you want to unwind your segue.
func updateStats(){ self.performSegueWithIdentifier("Unwind Segue", sender: self) }
In my case I wanted to do this every time the table was updated so I put it in my controllerDidChangeContent function.
func controllerDidChangeContent(controller: NSFetchedResultsController) { self.tableView.endUpdates() updateStats() }
Voila! Now my stats were updating each time my table was edited.