簡體   English   中英

添加視圖 controller 作為另一個視圖中的子視圖 controller

[英]Adding a view controller as a subview in another view controller

我發現這個問題的帖子很少,但沒有一個解決了我的問題。

說我有..

  1. 視圖控制器A
  2. 視圖控制器B

我試圖將 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 將離開屏幕。 灰色邊框是我為子視圖創建的框架。在此處輸入圖像描述

幾個觀察:

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

    相反,您希望在 Interface Builder 中為您的目標視圖控制器提供一個“故事板 id”,然后您可以使用instantiateViewController(withIdentifier:)來實例化它(並連接所有 IB 插座)。 在 Swift 3 中:

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

    您現在可以訪問該controllerview

  2. 但是如果你真的想做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 簡而言之,您需要確保您的視圖控制器層次結構與您的視圖層次結構保持同步,並且這些對addChilddidMove(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)

另請查看有關實現自定義容器視圖控制器的官方文檔:

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

本文檔為每條指令提供了更詳細的信息,還描述了如何添加轉換。

轉換為 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM