2014-12-03 20 views
40

Ho trovato alcuni post per questo problema ma nessuno di loro ha risolto il mio problema.Aggiunta di un controller di visualizzazione come subview in un altro controller di vista

Dire, come ho ..

  1. ViewControllerA
  2. ViewControllerB

Ho provato ad aggiungere ViewControllerB come una visualizzazione secondaria in ViewControllerA Ma, è gettando un errore come "fatal error: unexpectedly found nil while unwrapping an Optional value".

seguito è il codice ...

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 è solo un semplice schermo con un'etichetta in esso.

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

Con la soluzione suggerita dalle risposte degli utenti, ViewControllerB in ViewControllerA sta fuori dallo schermo. Il bordo grigio è il frame che ho creato per la sottoview. enter image description here

risposta

104

Un paio di osservazioni:

  1. Quando si crea un'istanza del secondo controller della vista, che si sta chiamando ViewControllerB(). Se quel controller di visualizzazione crea automaticamente la sua vista (che è insolita), ciò andrebbe bene. Ma la presenza dello IBOutlet suggerisce che la scena di questo secondo controller di visualizzazione è stata definita in Interface Builder, ma chiamando ViewControllerB(), non si sta dando allo storyboard la possibilità di creare un'istanza di quella scena e di collegare tutti i punti vendita. Pertanto, lo UILabel implicitamente non utilizzato è nil, con conseguente messaggio di errore.

    Invece, si vuole dare al controller di visualizzazione di destinazione uno "id storyboard" in Interface Builder e quindi è possibile utilizzare instantiateViewController(withIdentifier:) per istanziarlo (e collegare tutte le prese IB). In Swift 3:

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

    È ora possibile accedere a questa s viewcontroller'.

  2. Ma se si vuole veramente fare addSubview (ovvero non si sta passando alla scena successiva), si sta effettuando una pratica chiamata "visualizzazione del contenimento del controller". Non vuoi semplicemente semplicemente addSubview. Si vuole fare alcune chiamate aggiuntive controller della vista del contenitore, per es .:

    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) 
    

    Per ulteriori informazioni sul motivo per cui sono necessari questo addChildViewController e didMove(toParentViewController:), vedere WWDC 2011 video #102 - Implementing UIViewController Containment.In breve, è necessario assicurarsi che la gerarchia del controller di visualizzazione rimanga sincronizzata con la gerarchia della vista e queste chiamate a addChildViewController e didMove(toParentViewController:) assicurano che questo sia il caso.

    Vedere anche Creating Custom Container View Controllers nella Guida alla programmazione del controller View .


A proposito, quanto sopra illustra come fare questo livello di programmazione. In realtà è molto più semplice se si utilizza la "vista contenitore" in Interface Builder.

enter image description here

Allora non c'è bisogno di preoccuparsi di una di queste chiamate di contenimento legate, e Interface Builder si prenderà cura di esso per voi.

Per l'implementazione di Swift 2, vedere previous revision of this answer.

+1

Grazie per la spiegazione dettagliata. Quando ho provato ad aggiungere 'ViewControllerB' a' ViewControllerA', 'ViewControllerB' sta uscendo dallo schermo. Ho modificato il mio post con lo screenshot del simulatore. –

+0

Questo è possibile. Ecco perché, nel mio esempio, ho impostato il 'frame' manualmente. Oppure, se si disattiva 'translatesFrameIntoConstraints' (o come si chiama), è possibile aggiungere anche i vincoli a livello di programmazione. Ma se stai aggiungendo la sottoview, sei responsabile dell'impostazione del frame, in un modo o nell'altro, proprio come lo sei per tutte le visualizzazioni secondarie aggiunte a livello di codice. – Rob

+0

Ecco come è possibile aggiungere dei limiti 'controller.view.frame = UIScreen.mainScreen(). Bounds' –

32

Grazie a Rob. L'aggiunta di sintassi per la seconda osservazione:

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) 

E per rimuovere il viewcontroller:

self.willMoveToParentViewController(nil) 
self.view.removeFromSuperview() 
self.removeFromParentViewController() 
+4

BTW, quando si chiama 'addChildViewController', si chiama _non_' willMoveToParentViewController'. Chiamando 'addChildViewController' lo chiamerà per te. Vedi [Aggiunta e rimozione di un bambino] (https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingContainerViewControllers/CreatingCustomContainerViewControllers.html#//apple_ref/doc/uid/TP40007457-CH18-SW13) in _View Controller Programming Guide for iOS._ Per questo motivo, personalmente chiamerei sempre 'addChildViewController' immediatamente dopo averlo istanziato, ma prima di configurarlo. – Rob

+1

Quando si esegue controller.ANYPROPERTY = THEVALUE..Io penso che AnyProperty sia definita nel childViewController. Ho provato e mi stava dando un errore. Qualche idea su come correggerlo. –

+0

@Anuj Arora la tua ipotesi è giusta. ANYPROPERTY è definito in child viewController.È possibile controllare ANYPROPERTY in child viewcontroller ma in ViewDidAppear non in ViewDidLoad. – Sunita

0

Si prega di controllare anche la documentazione ufficiale per l'attuazione di una vista del contenitore di controllo personalizzato:

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

Questa documentazione ha informazioni molto più dettagliate per ogni istruzione e descrive anche come aggiungere transizioni.

Tradotto 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

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

     }) 
    } 
Problemi correlati