Agregar un controlador de vista como subvista en otro controlador de vista

He encontrado pocas publicaciones para este problema, pero ninguna resolvió mi problema.

Di como que yo ..

  1. ViewControllerA
  2. ViewControllerB

Traté de agregar ViewControllerB como una subvista en ViewControllerA, pero está arrojando un error como ” fatal error: unexpectedly found nil while unwrapping an Optional value “.

Debajo está el código …

ViewControllerA

 var testVC: ViewControllerB = ViewControllerB(); override func viewDidLoad() { super.viewDidLoad() self.testVC.view.frame = CGRectMake(0, 0, 350, 450); self.view.addSubview(testVC.view); // Do any additional setup after loading the view. } 

ViewControllerB es simplemente una pantalla simple con una etiqueta.

ViewControllerB

  @IBOutlet weak var test: UILabel! override func viewDidLoad() { super.viewDidLoad() test.text = "Success" // Throws ERROR here "fatal error: unexpectedly found nil while unwrapping an Optional value" } 

EDITAR

Con la solución sugerida por las respuestas del usuario, ViewControllerB en ViewControllerA se sale de la pantalla. Borde gris es el marco que he creado para la subvista. enter image description here

Un par de observaciones:

  1. Cuando crea una instancia del segundo controlador de vista, está llamando a ViewControllerB() . Si ese controlador de vista crea programáticamente su vista (lo cual es inusual), estaría bien. Pero la presencia de IBOutlet sugiere que la escena de este controlador de segunda vista se definió en Interface Builder, pero al llamar a ViewControllerB() , no le está dando al guión gráfico la oportunidad de crear una instancia de esa escena y conectar todas las salidas. Por lo tanto, el UILabel implícitamente UILabel es nil , lo que da como resultado su mensaje de error.

    En su lugar, desea darle a su controlador de vista de destino una “identificación del guión gráfico” en Interface Builder y luego puede usar instantiateViewController(withIdentifier:) para crear una instancia (y conectar todas las salidas IB). En Swift 3:

     let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id") 

    Ahora puede acceder a la vista de este controller .

  2. Pero si realmente desea addSubview (es decir, no está en transición a la siguiente escena), entonces está participando en una práctica llamada “view containment containment”. No solo quiere simplemente addSubview . Desea hacer algunas llamadas al controlador de vista de contenedor adicional, por ejemplo:

     let controller = storyboard.instantiateViewController(withIdentifier: "scene storyboard id") addChildViewController(controller) controller.view.frame = ... // or better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview view.addSubview(controller.view) controller.didMove(toParentViewController: self) 

    Para obtener más información acerca de por qué son necesarios addChildViewController y didMove(toParentViewController:) , consulte el video WWDC 2011 # 102 – Implementación de la contención de UIViewController . En resumen, debe asegurarse de que la jerarquía del controlador de vista permanezca sincronizada con su jerarquía de vistas, y estas llamadas a addChildViewController y didMove(toParentViewController:) aseguran que este es el caso.

    También vea Crear Controladores de Vista de Contenedor Personalizados en la Guía de Progtwigción del Controlador de Vista.


Por cierto, lo anterior ilustra cómo hacer esto programáticamente. En realidad, es mucho más fácil si usa la “vista de contenedor” en Interface Builder.

enter image description here

Entonces no tiene que preocuparse por ninguna de estas llamadas relacionadas con la contención, y Interface Builder se encargará de ello.

Para la implementación de Swift 2, consulte la revisión anterior de esta respuesta .

Gracias a Rob. Agregar syntax detallada para su segunda observación:

 let controller:MyView = self.storyboard!.instantiateViewControllerWithIdentifier("MyView") as! MyView controller.ANYPROPERTY=THEVALUE // If you want to pass value controller.view.frame = self.view.bounds; controller.willMoveToParentViewController(self) self.view.addSubview(controller.view) self.addChildViewController(controller) controller.didMoveToParentViewController(self) 

Y para eliminar viewcontroller:

 self.willMoveToParentViewController(nil) self.view.removeFromSuperview() self.removeFromParentViewController() 

func callForMenuView () {

  if(!isOpen) { isOpen = true let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "menu") as! MenuViewController self.view.addSubview(menuVC.view) self.addChildViewController(menuVC) menuVC.view.layoutIfNeeded() menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height); UIView.animate(withDuration: 0.3, animations: { () -> Void in menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height); }, completion:nil) }else if(isOpen) { isOpen = false let viewMenuBack : UIView = view.subviews.last! UIView.animate(withDuration: 0.3, animations: { () -> Void in var frameMenu : CGRect = viewMenuBack.frame frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width viewMenuBack.frame = frameMenu viewMenuBack.layoutIfNeeded() viewMenuBack.backgroundColor = UIColor.clear }, completion: { (finished) -> Void in viewMenuBack.removeFromSuperview() }) } 

Consulte también la documentación oficial sobre la implementación de un controlador de vista de contenedor personalizado:

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1

Esta documentación contiene información mucho más detallada para cada instrucción y también describe cómo agregar transiciones.

Traducido a Swift 3:

 func cycleFromViewController(oldVC: UIViewController, newVC: UIViewController) { // Prepare the two view controllers for the change. oldVC.willMove(toParentViewController: nil) addChildViewController(newVC) // Get the start frame of the new view controller and the end frame // for the old view controller. Both rectangles are offscreen.r newVC.view.frame = view.frame.offsetBy(dx: view.frame.width, dy: 0) let endFrame = view.frame.offsetBy(dx: -view.frame.width, dy: 0) // Queue up the transition animation. self.transition(from: oldVC, to: newVC, duration: 0.25, animations: { newVC.view.frame = oldVC.view.frame oldVC.view.frame = endFrame }) { (_: Bool) in oldVC.removeFromParentViewController() newVC.didMove(toParentViewController: self) } }