Embedding View Controllers

Table of Contents

Intro

Good code delegates responsibility to the objects responsible for that code. It just makes it easier for others to understand and reason about, especially in large projects.

However, a common practice is to place a UITableView as a view within a UIViewController and then set the delegate and dataSource of the table view to the view controller. In some situations, this is perfectly fine. However, if you want to segregate your code with a logical grouping, it’d make sense to have a UITableViewController manager a table view and a UIViewController manage a regular view. The catch is, how do you combine them when you want to have both on the same screen?

The answer is embedding! It’s actually quite simple.

Breakdown

  1. In this scenario, you have a UIViewController and UITableViewController
    • (by no means is this exclusive to this exact combo, but it is a common scenario)
    • embed start
  2. Place a Container View in the view controller you’d like to make the parent…
    1. find the container view
    2. Delete the additional View Controller it creates
    3. drag it onto your canvas
      • notice that it states that it’s simply a UIView. Under the hood, this is the case, but Interface Builder won’t work unless we use an explicit Container View :/
  3. Finally, {right-click}-drag or {control}-drag from the container view to the view controller you’d like to embed within. When you let go, choose embed

That’s it! Or, it can be if you don’t care about passing data back and forth. If you do, continue on!

Segue Setup

To get the two View Controllers to talk to each other, it won’t take too much work, but before we do that, you need to set an identifier on the segue between the two controllers:

segue identifier
You can set the identifer to anything you wish, just as long it’s unique amongst segue identifiers… But just be sure to name it something logical.

Here’s the code:

class ViewController: UIViewController {
    var embeddedTableViewController: EmbeddedTableViewController? { // you remembered to create and set the class for your table view controller, right?!
        didSet {
            embeddedTableViewController?.muhDatas = ["Yo dawg, I heard you like embedding view controllers, so I embedded a view controller in your view controller so you can control views while you control views."]
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "EmbedTableController" {
            guard let dest = segue.destination as? EmbeddedTableViewController else { return }
            embeddedTableViewController = dest
            // now I can access the embedded controller and give it data or make it respond to button presses or whatnot
        }
    }
}

In this code, the following happens:

  1. We create a variable to hold a reference to the embedded view controller (you remembered to subclass it, right?!)
    1. We add a didSet observer to this reference to immediately provide it with data after being set.
  2. The prepare(for segue) function is used to create the link between the two controllers.
  3. That’s really about it… Everything beyond this is an implementation detail. Set up data passing in a reasonable, logical way.

Cheat your way to success

If you’d like a copy of the code, it can be found on the embed-viewcontroller branch of my BlogDemo repo.

Now to make an InceptionController…

Leave a Reply