我发现这个问题的帖子很少,但没有一个能解决我的问题。
说得像我......
我尝试将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将离开屏幕。灰色边框是我为子视图创建的框架。
答案 0 :(得分:144)
有几点意见:
实例化第二个视图控制器时,您正在调用ViewControllerB()
。如果该视图控制器以编程方式创建其视图(这是不寻常的),那将没有问题。但是IBOutlet
的存在表明第二个视图控制器的场景是在Interface Builder中定义的,但是通过调用ViewControllerB()
,你不会给故事板提供实例化那个场景并挂钩所有出口的机会。因此,隐式展开的UILabel
为nil
,从而导致出现错误消息。
相反,您希望在Interface Builder中为目标视图控制器提供“故事板ID”,然后您可以使用instantiateViewController(withIdentifier:)
对其进行实例化(并连接所有IB插座)。在Swift 3中:
let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
您现在可以访问此controller
的{{1}}。
但如果你真的想做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。
顺便说一句,上面说明了如何以编程方式执行此操作。如果在Interface Builder中使用“容器视图”,实际上要容易得多。
然后您不必担心任何与这些包含相关的调用,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)
还请查看有关实现自定义容器视图控制器的官方文档:
本文档为每条指令提供了更详细的信息,并介绍了如何添加转换。
翻译成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()
}
}
}