ios - containerview - Agregar un controlador de vista como una subvista en otro controlador de vista
navigation controller swift 4 (7)
Consulte también la documentación oficial sobre la implementación de un controlador de vista de contenedor personalizado:
Esta documentación tiene información mucho más detallada para cada instrucción y también describe cómo agregar transiciones.
Traducido a 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)
}
}
He encontrado algunas publicaciones para este problema, pero ninguna de ellas resolvió mi problema.
Di como si hubiera ..
- ViewControllerA
- ViewControllerB
Intenté agregar ViewControllerB como una subvista en ViewControllerA, pero arroja un error como "
fatal error: unexpectedly found nil while unwrapping an Optional value
".
Debajo está el código ...
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 es solo una pantalla simple con una etiqueta.
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"
}
EDITAR
Con la solución sugerida por las respuestas del usuario, ViewControllerB en ViewControllerA está desapareciendo de la pantalla. El borde gris es el marco que he creado para la subvista.
Gracias a Rob Agregar sintaxis detallada para su segunda observación:
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)
Y para eliminar el viewcontroller:
self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController()
Gracias a Rob, sintaxis actualizada de Swift 4.2
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)
Para Agregar y quitar 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()
}
}
}
Un par de observaciones:
-
Cuando crea una instancia del segundo controlador de vista, está llamando a
ViewControllerB()
. Si ese controlador de vista crea programáticamente su vista (lo cual es inusual), estaría bien. Pero la presencia deIBOutlet
sugiere que la escena de este segundo controlador de vista se definió en Interface Builder, pero al llamar aViewControllerB()
, no le está dando al guión gráfico la oportunidad de instanciar esa escena y conectar todos los puntos de venta. Por lo tanto, elUILabel
envuelto implícitamente esnil
, lo que da como resultado su mensaje de error.En su lugar, desea darle a su controlador de vista de destino una "identificación del guión gráfico" en Interface Builder y luego puede usar
instantiateViewController(withIdentifier:)
para instanciarlo (y conectar todos los puntos de venta IB). En Swift 3:let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
Ahora puede acceder a la vista de este
controller
. -
Pero si realmente desea
addSubview
(es decir, no está haciendo la transición a la siguiente escena), entonces está participando en una práctica llamada "contención del controlador de vista". No solo deseaaddSubview
simplementeaddSubview
. Desea hacer algunas llamadas adicionales del controlador de vista de contenedor, por ejemplo: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)
Para obtener más información acerca de por qué este
addChild
(anteriormente llamadoaddChildViewController
) ydidMove(toParent:)
(anteriormente llamadodidMove(toParentViewController:)
) son necesarios, vea el video # 102 de WWDC 2011 - Implementación de UIViewController Containment . En resumen, debe asegurarse de que la jerarquía de su controlador de vista permanezca sincronizada con su jerarquía de vista, y estas llamadas aaddChild
ydidMove(toParent:)
aseguran que este sea el caso.Consulte también Creación de controladores de vista de contenedor personalizados en la Guía de programación del controlador de vista.
Por cierto, lo anterior ilustra cómo hacer esto mediante programación. En realidad, es mucho más fácil si usa la "vista de contenedor" en Interface Builder.
Entonces no tiene que preocuparse por ninguna de estas llamadas relacionadas con la contención, e Interface Builder se encargará de usted.
Para la implementación de Swift 2, consulte la revisión anterior de esta respuesta .
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()
})
}
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)