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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27276561/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:53:12  来源:igfitidea点击:

Adding a view controller as a subview in another view controller

iosswiftuiviewcontroller

提问by Srujan Simha

I have found few posts for this problem but none of them solved my issue.

我找到了几个关于这个问题的帖子,但没有一个帖子解决了我的问题。

Say like I've..

像我一样说..

  1. ViewControllerA
  2. ViewControllerB
  1. 视图控制器A
  2. 视图控制器B

I tried to add ViewControllerB as a subview in ViewControllerA but, it's throwing an error like "fatal error: unexpectedly found nil while unwrapping an Optional value".

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

Below is the code...

下面是代码...

ViewControllerA

视图控制器A

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 is just a simple screen with a label in it.

ViewControllerB 只是一个带有标签的简单屏幕。

ViewControllerB

视图控制器B

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

EDIT

编辑

With the suggested solution from the user answers, ViewControllerB in ViewControllerA is going off the screen. Grey border is the frame I have created for the subview. enter image description here

根据用户答案中的建议解决方案,ViewControllerA 中的 ViewControllerB 将离开屏幕。灰色边框是我为子视图创建的框架。 在此处输入图片说明

回答by Rob

A couple of observations:

几个观察:

  1. 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 the IBOutletsuggests that this second view controller's scene was defined in Interface Builder, but by calling ViewControllerB(), you are not giving the storyboard a chance to instantiate that scene and hook up all the outlets. Thus the implicitly unwrapped UILabelis nil, 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's view.

  2. 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 simply addSubview. 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 called addChildViewController) and didMove(toParent:)(previously called didMove(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 to addChildand didMove(toParent:)ensure this is the case.

    Also see Creating Custom Container View Controllersin the View Controller Programming Guide.

  1. 当您实例化第二个视图控制器时,您正在调用ViewControllerB(). 如果该视图控制器以编程方式创建其视图(这是不寻常的),那就没问题了。但是 的存在IBOutlet表明第二个视图控制器的场景是在 Interface Builder 中定义的,但是通过调用ViewControllerB(),您并没有给情节提要机会实例化该场景并连接所有插座。因此,隐式解包UILabelis nil,导致您的错误消息。

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

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

    现在,您可以访问此controllerview

  2. 但是如果你真的想做addSubview(即你没有过渡到下一个场景),那么你正在从事一种称为“视图控制器包含”的实践。你不只是想简单addSubview。你想做一些额外的容器视图控制器调用,例如:

    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)
    

    有关为什么需要此addChild(以前称为addChildViewController)和didMove(toParent:)(以前称为didMove(toParentViewController:))的更多信息,请参阅WWDC 2011 视频 #102 - 实现 UIViewController Containment。简而言之,您需要确保您的视图控制器层次结构与您的视图层次结构保持同步,并且这些调用addChilddidMove(toParent:)确保是这种情况。

    另请参阅视图控制器编程指南中的创建自定义容器视图控制器



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.

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

enter image description here

在此处输入图片说明

Then you don't have to worry about any of these containment-related calls, and Interface Builder will take care of it for you.

那么您就不必担心任何这些与包含相关的调用,Interface Builder 会为您处理。

For Swift 2 implementation, see previous revision of this answer.

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

回答by SML

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;
controller.willMoveToParentViewController(self)
self.view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)

And to remove the viewcontroller :

并删除视图控制器:

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

回答by Nirbhay Singh

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)

回答by jaya

func callForMenuView() {

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

        })
    }

回答by Emre Gürses

Thanks to Rob, Updated Swift 4.2 syntax

感谢 Rob,更新了 Swift 4.2 语法

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

回答by Simon Backx

Please also check the official documentation on implementing a custom container view controller:

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

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

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

This documentation has much more detailed information for every instruction and also describes how to do add transitions.

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

Translated to Swift 3:

转换为 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)
    }
}

回答by krishnan

For Add and Remove ViewController

用于添加和删除 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()
        }

    }
}