Traditionally in iOS apps a view controller corresponds to one "screen" in the application. Apple provides a few container view controllers—such as the navigation controller and tab bar controller—to help developers manage and transition between multiple view controllers.
To build really unique user experiences, you can also define your own
custom container view controllers that manage the display of and
navigation between child view controllers. This guide provides a
quick start recipe for doing this by building a simple container view
controller TwoTabViewController
that allows the user to switch between
two view controllers.
Create a new subclass of UIViewController
by selecting File -> New ->
File... -> Source -> Cocoa Touch Class
. Tick the checkbox named Also
create XIB file
.
You'll generally want a way for the user to control or navigate between
the child view controllers. You'll probably also want add one or more
blank view elements to serve as placeholders for the views of the child
view controllers. Add @IBOutlets
for any components you'll need
programatically manipulate and @IBActions
for any events to which you
want to respond.
You'll need a way to track of all the view controllers you are "containing". For example the navigation controller maintains a stack of view controllers. In our two tab example, we keep a separate property to reference each of the child view controllers.
class TwoTabViewController: UIViewController {
...
var firstViewController: UIViewController?
var secondViewController: UIViewController?
...
}
The main responsibility of a container view controller is to display the
child view controllers' views. You'll need to add code to keep track of
which child views are currently being shown. You'll also need help iOS
propagate events (such as viewDidLoad
) to the child view controllers
by calling certain methods before and after adding/removing a child view
controller's view.
In our two tab example we keep a variable activeViewController
.
Whenever activeViewController
is set we remove the old one's view and
add the new one's view as a subview of our content view.
class TwoTabViewController: UIViewController {
...
private var activeViewController: UIViewController? {
didSet {
removeInactiveViewController(inactiveViewController: oldValue)
updateActiveViewController()
}
}
private func removeInactiveViewController(inactiveViewController: UIViewController?) {
if let inActiveVC = inactiveViewController {
// call before removing child view controller's view from hierarchy
inActiveVC.willMove(toParentViewController: nil)
inActiveVC.view.removeFromSuperview()
// call after removing child view controller's view from hierarchy
inActiveVC.removeFromParentViewController()
}
}
private func updateActiveViewController() {
if let activeVC = activeViewController {
// call before adding child view controller's view as subview
addChildViewController(activeVC)
activeVC.view.frame = contentView.bounds
contentView.addSubview(activeVC.view)
// call before adding child view controller's view as subview
activeVC.didMove(toParentViewController: self)
}
}
...
}
You'll often have update which child view controllers' views are being displayed in response to system or user events. Here we set the active view controller to the be first one when our container view controller loads. We also update the active view controller in response to user taps.
class TwoTabViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
activeViewController = firstViewController
}
@IBAction func didTapFirstButton(sender: AnyObject) {
activeViewController = firstViewController
}
@IBAction func didTapSecondButton(sender: AnyObject) {
activeViewController = secondViewController
}
}
As of this writing, there is no easy way to use and configure custom container view controllers in a storyboard. Your best bet is to instantiate and configure any custom container view controllers programmatically. If your custom container is the root view controller, a good place to do this is in the app delegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let vc1 = UIViewController()
let vc2 = UIViewController()
vc1.view.backgroundColor = UIColor.orangeColor()
vc2.view.backgroundColor = UIColor.purpleColor()
let twoTabVC = TwoTabViewController(nibName: "TwoTabViewController", bundle: nil)
twoTabVC.firstViewController = vc1
twoTabVC.secondViewController = vc2
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.rootViewController = twoTabVC
window?.makeKeyAndVisible()
return true
}
...
}