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

时间:2014-12-03 16:17:27

标签: ios swift uiviewcontroller

我发现这个问题的帖子很少,但没有一个能解决我的问题。

说得像我......

  1. ViewControllerA
  2. ViewControllerB
  3. 我尝试将ViewControllerB添加为ViewControllerA中的子视图,但是它会抛出类似“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"
    }
    

    修改

    使用来自用户答案的​​建议解决方案,ViewControllerA中的ViewControllerB将离开屏幕。灰色边框是我为子视图创建的框架。 enter image description here

7 个答案:

答案 0 :(得分:144)

有几点意见:

  1. 实例化第二个视图控制器时,您正在调用ViewControllerB()。如果该视图控制器以编程方式创建其视图(这是不寻常的),那将没有问题。但是IBOutlet的存在表明第二个视图控制器的场景是在Interface Builder中定义的,但是通过调用ViewControllerB(),你不会给故事板提供实例化那个场景并挂钩所有出口的机会。因此,隐式展开的UILabelnil,从而导致出现错误消息。

    相反,您希望在Interface Builder中为目标视图控制器提供“故事板ID”,然后您可以使用instantiateViewController(withIdentifier:)对其进行实例化(并连接所有IB插座)。在Swift 3中:

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

    您现在可以访问此controller的{​​{1}}。

  2. 但如果你真的想做view(即你没有转换到下一个场景),那么你正在进行一种名为“视图控制器遏制”的练习。您不仅仅想要addSubview。您想要进行一些额外的容器视图控制器调用,例如:

    addSubview

    有关为何需要此addChild(以前称为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) )和didMove(toParent:)(以前称为addChildViewController)的详细信息,请参阅WWDC 2011 video #102 - Implementing UIViewController Containment。简而言之,您需要确保视图控制器层次结构与视图层次结构保持同步,并且这些对didMove(toParentViewController:)addChild的调用确保是这种情况。

    另请参阅 View Controller编程指南中的Creating Custom Container View Controllers


  3. 顺便说一句,上面说明了如何以编程方式执行此操作。如果在Interface Builder中使用“容器视图”,实际上要容易得多。

    enter image description here

    然后您不必担心任何与这些包含相关的调用,Interface Builder将为您处理它。

    对于Swift 2的实施,请参阅previous revision of this answer

答案 1 :(得分:39)

感谢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)

删除viewcontroller:

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

答案 2 :(得分:2)

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

        })
    }

答案 3 :(得分:2)

This code will work for Swift 4.2.

let controller:SecondViewController = 
self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as! 
SecondViewController
controller.view.frame = self.view.bounds;
controller.willMove(toParent: self)
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)

答案 4 :(得分:1)

感谢Rob,更新了Swift 4.2语法

return render(request, 'page.html')

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

用于添加和删除ViewController

 var secondViewController :SecondViewController?

  // Adding 
 func add_ViewController() {
    let controller  = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController")as! SecondViewController
    controller.view.frame = self.view.bounds;
    controller.willMove(toParent: self)
    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()
        }

    }
}