在另一个视图控制器中添加视图控制器作为子视图
我发现这个问题很less的post,但没有一个解决了我的问题。
像我一样说
- ViewControllerA
- 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的框架。
几点意见:
-
当你实例化第二个视图控制器时,你正在调用
ViewControllerB()
。 如果该视图控制器以编程方式创build其视图(这是不寻常的),这将是很好的。 但是IBOutlet
的存在意味着这个第二个视图控制器的场景是在Interface Builder中定义的,但是通过调用ViewControllerB()
,你不会给Storyboard一个实例化这个场景的机会,并且连接所有的sockets。 因此,隐含的解包UILabel
是nil
,导致你的错误信息。相反,你想在接口生成器中给你的目的视图控制器一个“storyboard id”,然后你可以使用
instantiateViewController(withIdentifier:)
来实例化它(并且连接所有的IBsockets)。 在Swift 3:let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
你现在可以访问这个
controller
的view
。 -
但是,如果你真的想做
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)
有关为什么需要此
addChildViewController
和didMove(toParentViewController:)
更多信息,请参阅WWDC 2011video#102 – 实现UIViewController Containment 。 总之,您需要确保您的视图控制器层次结构与您的视图层次结构保持同步,并且这些调用addChildViewController
和didMove(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()
请检查关于实现自定义容器视图控制器的官方文档:
本文档提供了更详细的每条指令信息,还介绍了如何添加转换。
翻译成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() }) }