Adding a view controller as a subview in another view controller
Solution 1:
A couple of observations:
-
When you instantiate the second view controller, you are calling
ViewControllerB()
. If that view controller programmatically creates its view (which is unusual) that would be fine. But the presence of theIBOutlet
suggests that this second view controller's scene was defined in Interface Builder, but by callingViewControllerB()
, you are not giving the storyboard a chance to instantiate that scene and hook up all the outlets. Thus the implicitly unwrappedUILabel
isnil
, resulting in your error message.Instead, you want to give your destination view controller a "storyboard id" in Interface Builder and then you can use
instantiateViewController(withIdentifier:)
to instantiate it (and hook up all of the IB outlets). In Swift 3:let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
You can now access this
controller
'sview
. -
But if you really want to do
addSubview
(i.e. you're not transitioning to the next scene), then you are engaging in a practice called "view controller containment". You do not just want to simplyaddSubview
. You want to do some additional container view controller calls, e.g.:let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id") addChild(controller) controller.view.frame = ... // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview view.addSubview(controller.view) controller.didMove(toParent: self)
For more information about why this
addChild
(previously calledaddChildViewController
) anddidMove(toParent:)
(previously calleddidMove(toParentViewController:)
) are necessary, see WWDC 2011 video #102 - Implementing UIViewController Containment. In short, you need to ensure that your view controller hierarchy stays in sync with your view hierarchy, and these calls toaddChild
anddidMove(toParent:)
ensure this is the case.Also see Creating Custom Container View Controllers in the View Controller Programming Guide.
By the way, the above illustrates how to do this programmatically. It is actually much easier if you use the "container view" in Interface Builder.
Then you don't have to worry about any of these containment-related calls, and Interface Builder will take care of it for you.
For Swift 2 implementation, see previous revision of this answer.
Solution 2:
Thanks to Rob. Adding detailed syntax for your second observation :
let controller:MyView = self.storyboard!.instantiateViewControllerWithIdentifier("MyView") as! MyView
controller.ANYPROPERTY=THEVALUE // If you want to pass value
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)
And to remove the viewcontroller :
self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController()
Solution 3:
This code will work for Swift 4.2.
let controller = self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as! SecondViewController
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)
Solution 4:
For Add and Remove ViewController
var secondViewController :SecondViewController?
// Adding
func add_ViewController() {
let controller = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController")as! SecondViewController
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)
self.secondViewController = controller
}
// Removing
func remove_ViewController(secondViewController:SecondViewController?) {
if secondViewController != nil {
if self.view.subviews.contains(secondViewController!.view) {
secondViewController!.view.removeFromSuperview()
}
}
}