2014-12-03 118 views
40

我找到了這個問題的幾篇文章,但沒有一篇解決了我的問題。在另一個視圖控制器中添加視圖控制器作爲子視圖

說我好象..

  1. ViewControllerA
  2. ViewControllerB

我試圖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" 
} 

EDIT

隨着來自用戶的答案的建議的解決方案,在ViewControllerB ViewControllerA會在屏幕上。灰色邊框是我爲子視圖創建的框架。 enter image description here

回答

104

一對夫婦的意見:

  1. 當你實例第二個視圖控制器,您呼叫ViewControllerB()。如果該視圖控制器以編程方式創建其視圖(這是不尋常的),那就沒問題了。但是IBOutlet的存在表明這個第二個視圖控制器的場景是在Interface Builder中定義的,但是通過調用ViewControllerB(),您不會給故事板一個實例化該場景並掛接所有插座的機會。因此,隱含地解包UILabelnil,導致您的錯誤消息。

    取而代之,您希望在Interface Builder中爲您的目標視圖控制器提供「故事板ID」,然後您可以使用instantiateViewController(withIdentifier:)實例化它(並掛接所有IB插座)。在斯威夫特3:

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

    您現在可以訪問此controllerview。但如果你真的想要做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) 
    

    有關爲什麼這個addChildViewControllerdidMove(toParentViewController:)是必要的,看到WWDC 2011 video #102 - Implementing UIViewController Containment更多信息。簡而言之,您需要確保您的視圖控制器層次結構與您的視圖層次結構保持同步,並且這些對addChildViewControllerdidMove(toParentViewController:)的調用確保這種情況。

    另請參閱「視圖控制器編程指南」中的Creating Custom Container View Controllers


順便說一句,上面說明了如何以編程方式做到這一點。如果您在Interface Builder中使用「容器視圖」,實際上它會容易得多。

enter image description here

那麼你不必擔心這些遏制相關的電話,和Interface Builder會照顧它。

對於Swift 2的實現,請參閱previous revision of this answer

+1

感謝您的詳細解釋。當我嘗試將'ViewControllerB'添加到'ViewControllerA'時,'ViewControllerB'正在關閉屏幕。我用模擬器的屏幕截圖編輯了我的帖子。 – 2014-12-04 17:01:15

+0

這是可能的。這就是爲什麼,在我的例子中,我手動設置了「框架」。或者,如果你關閉了'translatesFrameIntoConstraints'(或者其他所謂的),你也可以通過編程來添加約束條件。但是,如果您添加子視圖,則需要負責設置其框架,就像您使用所有以編程方式添加的子視圖一樣。 – Rob 2014-12-04 18:31:58

+0

這是如何添加邊界controller.view.frame = UIScreen.mainScreen()。bounds' – 2016-03-03 09:14:53

32

感謝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() 
+4

順便說一句,當調用'addChildViewController'時,不_調用'willMoveToParentViewController'。調用'addChildViewController'會爲你調用它。請參閱_View中的[添加和刪除子項](https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html#//apple_ref/doc/uid/TP40007457-CH18-SW13)針對iOS的控制器編程指南_因此,我個人總是在實例化之後立即調用'addChildViewController',但在配置它之前。 – Rob 2015-08-17 12:47:45

+1

當你做的controller.ANYPROPERTY = THEVALUE ..我猜AnyProperty是在childViewController中定義的。我試過了,它給了我一個錯誤。任何想法如何糾正。 – 2015-10-08 08:15:16

+0

@Anuj Arora你的猜測是對的。 ANYPROPERTY在子viewController中定義。您可以在子ViewController中檢查ANYPROPERTY,但在ViewDidAppear中不檢查ViewDidLoad中的ANYPROPERTY。 – Sunita 2016-05-19 11:17:01

0

同時請參閱官方文檔上實現自定義的容器視圖控制器:

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

本文檔包含每條指令的更多詳細信息還介紹瞭如何添加轉換。

翻譯斯威夫特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) 
    } 
} 
0

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

     }) 
    } 
相關問題