[英]Adding a view controller as a subview in another view controller
我發現這個問題的帖子很少,但沒有一個解決了我的問題。
說我有..
我試圖將 ViewControllerB 添加為 ViewControllerA 中的子視圖,但它拋出了一個錯誤,如“ fatal error: unexpectedly found nil while unwrapping an Optional value
”。
下面是代碼...
視圖控制器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 只是一個帶有 label 的簡單屏幕。
視圖控制器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"
}
編輯
根據用戶回答的建議解決方案,ViewControllerA 中的 ViewControllerB 將離開屏幕。 灰色邊框是我為子視圖創建的框架。
幾個觀察:
當您實例化第二個視圖控制器時,您正在調用ViewControllerB()
。 如果該視圖控制器以編程方式創建其視圖(這是不尋常的),那就沒問題了。 但是IBOutlet
的存在表明第二個視圖控制器的場景是在 Interface Builder 中定義的,但是通過調用ViewControllerB()
,您沒有給情節提要機會實例化該場景並連接所有插座。 因此,隱式解包的UILabel
為nil
,導致您的錯誤消息。
相反,您希望在 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:)
調用確保了這種情況。
另請參閱視圖控制器編程指南中的創建自定義容器視圖控制器。
順便說一下,上面說明了如何以編程方式執行此操作。 如果您在 Interface Builder 中使用“容器視圖”,實際上要容易得多。
那么您就不必擔心任何這些與包含相關的調用,Interface Builder 會為您處理。
對於 Swift 2 實現,請參閱此答案的先前修訂版。
感謝羅布。 為您的第二個觀察添加詳細語法:
let controller:MyView = self.storyboard!.instantiateViewControllerWithIdentifier("MyView") as! MyView
controller.ANYPROPERTY=THEVALUE // If you want to pass value
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)
並刪除視圖控制器:
self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController()
此代碼適用於 Swift 4.2。
let controller = self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as! SecondViewController
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)
用於添加和刪除 ViewController
var secondViewController :SecondViewController?
// Adding
func add_ViewController() {
let controller = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController")as! SecondViewController
controller.view.frame = self.view.bounds
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()
}
}
}
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()
})
}
感謝 Rob,更新了 Swift 4.2 語法
let controller:WalletView = self.storyboard!.instantiateViewController(withIdentifier: "MyView") as! WalletView
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)
另請查看有關實現自定義容器視圖控制器的官方文檔:
本文檔為每條指令提供了更詳細的信息,還描述了如何添加轉換。
轉換為 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)
}
}
Swift 5.1
加上:
let controller = storyboard?.instantiateViewController(withIdentifier: "MyViewControllerId")
addChild(controller!)
controller!.view.frame = self.containerView.bounds
self.containerView.addSubview((controller?.view)!)
controller?.didMove(toParent: self)
去除:
self.containerView.subviews.forEach({$0.removeFromSuperview()})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.