在另一个视图控制器中添加视图控制器作为子视图

我发现这个问题很less的post,但没有一个解决了我的问题。

像我一样说

  1. ViewControllerA
  2. ViewControllerB

我试图在ViewControllerA中添加ViewControllerB作为一个子视图,但是,它抛出了一个错误,如“ fatal error: unexpectedly found nil while unwrapping an Optional value ”。

以下是代码…

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只是一个带有标签的简单屏幕。

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" } 

编辑

从用户的答案build议的解决scheme,ViewControllerA中的ViewControllerB将离开屏幕。 灰色边框是我为子视图创build的框架。 在这里输入图像说明

几点意见:

  1. 当你实例化第二个视图控制器时,你正在调用ViewControllerB() 。 如果该视图控制器以编程方式创build其视图(这是不寻常的),这将是很好的。 但是IBOutlet的存在意味着这个第二个视图控制器的场景是在Interface Builder中定义的,但是通过调用ViewControllerB() ,你不会给Storyboard一个实例化这个场景的机会,并且连接所有的sockets。 因此,隐含的解包UILabelnil ,导致你的错误信息。

    相反,你想在接口生成器中给你的目的视图控制器一个“storyboard id”,然后你可以使用instantiateViewController(withIdentifier:)来实例化它(并且连接所有的IBsockets)。 在Swift 3:

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

    你现在可以访问这个controllerview

  2. 但是,如果你真的想做addSubview (即你不转换到下一个场景),那么你正在从事一个名为“视图控制器遏制”的做法。 你不只是想简单地添加子addSubview 。 你想做一些额外的容器视图控制器调用,例如:

     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) 

    有关为什么需要此addChildViewControllerdidMove(toParentViewController:)更多信息,请参阅WWDC 2011video#102 – 实现UIViewController Containment 。 总之,您需要确保您的视图控制器层次结构与您的视图层次结构保持同步,并且这些调用addChildViewControllerdidMove(toParentViewController:)确保是这种情况。

    另请参阅“ View Controller编程指南”中的 创build自定义容器视图控制器 ”。


顺便说一句,上面说明了如何以编程方式做到这一点。 如果你在界面生成器中使用“容器视图”,实际上它会容易得多。

在这里输入图像说明

然后,您不必担心这些与遏制相关的调用,Interface Builder将为您处理。

对于Swift 2实现,请参阅此答案的以前的修订 。

感谢Rob。 为您的第二个观察添加详细的语法:

 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) 

并删除视图控制器:

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

请检查关于实现自定义容器视图控制器的官方文档:

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

本文档提供了更详细的每条指令信息,还介绍了如何添加转换。

翻译成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) } } 

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() }) }