it-swarm.com.de

Hinzufügen eines View-Controllers als Unteransicht in einem anderen View-Controller

Ich habe einige Beiträge für dieses Problem gefunden, aber keines davon löste mein Problem.

Sag wie ich ..

  1. ViewControllerA
  2. ViewControllerB

Ich habe versucht, ViewControllerB als Unteransicht in ViewControllerA hinzuzufügen, aber es wird eine Fehlermeldung wie "fatal error: unexpectedly found nil while unwrapping an Optional value" ausgegeben.

Unten ist der Code ...

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 ist nur ein einfacher Bildschirm mit einer Beschriftung.

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

Mit der vorgeschlagenen Lösung aus den Benutzerantworten wird ViewControllerB in ViewControllerA vom Bildschirm genommen. Der graue Rahmen ist der Rahmen, den ich für die Unteransicht erstellt habe.enter image description here

54
Srujan Simha

Einige Beobachtungen:

  1. Wenn Sie den zweiten Ansichtscontroller instanziieren, rufen Sie ViewControllerB() auf. Wenn dieser Ansichtscontroller seine Ansicht programmgesteuert erstellt (was ungewöhnlich ist), ist dies in Ordnung. Das Vorhandensein von IBOutlet deutet jedoch darauf hin, dass die Szene dieses zweiten View-Controllers in Interface Builder definiert wurde. Wenn Sie jedoch ViewControllerB() aufrufen, können Sie dem Storyboard nicht die Möglichkeit geben, diese Szene zu instanziieren und eine Verbindung herzustellen Alle Verkaufsstellen. Das implizit ausgepackte UILabel ist also nil, was zu Ihrer Fehlermeldung führt.

    Stattdessen möchten Sie Ihrem Zielansichts-Controller eine "Storyboard-ID" in Interface Builder zuweisen und können sie dann mit instantiateViewController(withIdentifier:) instanziieren (und alle IB-Ausgänge anschließen). In Swift 3:

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

    Sie können jetzt auf das controller des view zugreifen.

  2. Wenn Sie jedoch wirklich addSubview ausführen möchten (d. H. Wenn Sie nicht zur nächsten Szene übergehen), führen Sie eine Übung namens "View Controller Containment" durch. Sie möchten nicht einfach nur addSubview. Sie möchten einige zusätzliche Container-View-Controller-Aufrufe ausführen, z.

    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)
    

    Weitere Informationen zum Grund dieser addChild (zuvor addChildViewController) und didMove(toParent:) (zuvor didMove(toParentViewController:)) sind notwendig, siehe WWDC 2011 Video # 102 - Implementierung von UIViewController Containment . Kurz gesagt, Sie müssen sicherstellen, dass Ihre View-Controller-Hierarchie mit Ihrer View-Hierarchie synchron bleibt, und diese Aufrufe von addChild und didMove(toParent:) stellen sicher, dass dies der Fall ist.

    Siehe auch Erstellen benutzerdefinierter Container-View-Controller im View Controller-Programmierhandbuch.


Im Übrigen wird im obigen Beispiel veranschaulicht, wie dies programmgesteuert durchgeführt wird. Es ist tatsächlich viel einfacher, wenn Sie die "Container-Ansicht" im Interface Builder verwenden.

enter image description here

Dann müssen Sie sich nicht um diese einschließungsbezogenen Aufrufe kümmern, und Interface Builder kümmert sich darum.

Informationen zur Implementierung von Swift 2) finden Sie unter vorherige Überarbeitung dieser Antwort .

148
Rob

Danke an Rob . Hinzufügen einer detaillierten Syntax für Ihre zweite Beobachtung: 

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)

Und um den Viewcontroller zu entfernen:

self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController() 
37
SML
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)
2
Nirbhay Singh

Dank Rob, Aktualisierte Swift 4.2-Syntax

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)
1
Emre Gürses

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

        })
    }
1
jaya

Bitte überprüfen Sie auch die offizielle Dokumentation zur Implementierung eines benutzerdefinierten Container-View-Controllers:

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

Diese Dokumentation enthält zu jeder Anweisung detailliertere Informationen und beschreibt, wie Sie Übergänge hinzufügen.

Übersetzt in 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)
    }
}
0
Simon Backx

Zum Hinzufügen und Entfernen von 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()
        }

    }
}
0
krishnan