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
Adding a view controller as a subview in another view controller
提问by Srujan Simha
I have found few posts for this problem but none of them solved my issue.
我找到了几个关于这个问题的帖子,但没有一个帖子解决了我的问题。
Say like I've..
像我一样说..
- ViewControllerA
- ViewControllerB
- 视图控制器A
- 视图控制器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.
根据用户答案中的建议解决方案,ViewControllerA 中的 ViewControllerB 将离开屏幕。灰色边框是我为子视图创建的框架。
回答by Rob
A couple of observations:
几个观察:
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 theIBOutlet
suggests that this second view controller's scene was defined in Interface Builder, but by callingViewControllerB()
, you are not giving the storyboard a chance to instantiate that scene and hook up all the outlets. Thus the implicitly unwrappedUILabel
isnil
, 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
'sview
.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 simplyaddSubview
. 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 calledaddChildViewController
) anddidMove(toParent:)
(previously calleddidMove(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 toaddChild
anddidMove(toParent:)
ensure this is the case.Also see Creating Custom Container View Controllersin the View Controller Programming Guide.
当您实例化第二个视图控制器时,您正在调用
ViewControllerB()
. 如果该视图控制器以编程方式创建其视图(这是不寻常的),那就没问题了。但是 的存在IBOutlet
表明第二个视图控制器的场景是在 Interface Builder 中定义的,但是通过调用ViewControllerB()
,您并没有给情节提要机会实例化该场景并连接所有插座。因此,隐式解包UILabel
isnil
,导致您的错误消息。相反,您希望在 Interface Builder 中为您的目标视图控制器提供一个“故事板 id”,然后您可以使用
instantiateViewController(withIdentifier:)
它来实例化它(并连接所有 IB 插座)。在 Swift 3 中:let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
现在,您可以访问此
controller
的view
。但是如果你真的想做
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。简而言之,您需要确保您的视图控制器层次结构与您的视图层次结构保持同步,并且这些调用addChild
并didMove(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 中使用“容器视图”,实际上要容易得多。
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:
另请查看有关实现自定义容器视图控制器的官方文档:
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()
}
}
}