Storyboard: consulte ViewController en AppDelegate

122

considere el siguiente escenario: tengo una aplicación basada en storyboard. Agrego un objeto ViewController al guión gráfico, agrego los archivos de clase para este ViewController en el proyecto y especifico el nombre de la nueva clase en el inspector de identidad de IB. Ahora, ¿cómo voy a referirme a este ViewController programáticamente desde AppDelegate? Hice una variable con la clase relevante y la convertí en una propiedad IBOutlet, pero no veo ninguna forma de poder hacer referencia al nuevo ViewController en código; cualquier intento de Ctrl-arrastrar una conexión no funciona .

es decir, dentro del AppDelegate puedo acceder al ViewController base de esta manera

(MyViewController*) self.window.rootViewController

pero ¿qué tal cualquier otro ViewController contenido en el guión gráfico?

Matthias D
fuente
Mira esta respuesta . Es rápido, pero los idiomas son similares.
Borzh

Respuestas:

165

Echa un vistazo a la documentación para -[UIStoryboard instantiateViewControllerWithIdentifier:]. Esto le permite crear una instancia de un controlador de vista de su guión gráfico utilizando el identificador que configuró en el Inspector de atributos de IB:

ingrese la descripción de la imagen aquí

EDITADO para agregar código de ejemplo:

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                         bundle: nil];

MyViewController *controller = (MyViewController*)[mainStoryboard 
                    instantiateViewControllerWithIdentifier: @"<Controller ID>"];
Robin Summerhill
fuente
Hola Robin, gracias por eso! Miré este documento pero confundí las palabras instanciar e inicializar ... esto nos lleva allí (después de seguir sus instrucciones :) (maldita sea la falta de formato de código en las respuestas ...) UIStoryboard * mainStoryboard = [UIStoryboard storyboardWithName: @ Paquete "MainStoryboard": nil]; MyViewController * thisController = (MyViewController *) [mainStoryboard instantiateViewControllerWithIdentifier: @ "myvc"];
Matthias D
He agregado su código de ejemplo a la respuesta con formato para cualquiera que esté mirando esto.
Robin Summerhill
24
si está creando una aplicación universal, asegúrese de usar MainStoryboard_iPhone / MainStoryboard_iPad, de lo contrario, se bloqueará.
roocell
16
Desde el delegado, puede acceder a la instancia del guión gráfico cargada por su lista de información de la siguiente manera: [[[self window] rootViewController] storyboard] Según los documentos, esto devolverá el "guión gráfico desde el que se originó el controlador de vista". (o nulo si no proviene de un guión gráfico). Desde ese UIStoryboard * puede usar las llamadas de instancia que mencionó @RobinSummerhill. Tenga en cuenta que Storyboards crea instancias de nuevas instancias de sus viewControllers ( escenas ) según sean necesarias y no reutiliza las que se vieron anteriormente.
Tad Bumcrot
66
Creo que el OP quiere saber CÓMO LLEGAR AL VC ACTUALMENTE EN EJECUCIÓN. No cómo cargar uno. Exactamente como él explica, solías poder decir esto self.window.rootViewController y ahora no puedes más.
Fattie
41

Si usa XCode5, debe hacerlo de una manera diferente.

  • Seleccione su UIViewControllerenUIStoryboard
  • Vaya al Identity Inspectorpanel superior derecho
  • Marque la Use Storyboard IDcasilla
  • Escribe una identificación única en el Storyboard IDcampo

Luego escribe tu código.

// Override point for customization after application launch.

if (<your implementation>) {
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" 
                                                             bundle: nil];
    YourViewController *yourController = (YourViewController *)[mainStoryboard 
      instantiateViewControllerWithIdentifier:@"YourViewControllerID"];
    self.window.rootViewController = yourController;
}

return YES;
Faruk Toptas
fuente
44
Hasta donde yo sé y pude probarlo, esta es la respuesta actualizada.
gnclmorais
El nombre de un guión gráfico es su nombre de archivo sin la extensión, por lo que también podría ser "Main_iPhone" o "Main_iPad" si ha configurado su proyecto de esa manera.
Brian White
y luego, ¿cómo hacemos la transición de regreso al rootViewController original después de iniciar sesión? Al menos para mi conocimiento de iOS 7 y Xcode 5.0.2: cambiar de nuevo el controlador de vista raíz será inmediato y sin animación cambiará la jerarquía de vista. El equipo de UX ha asesinado a codificadores por menos.
lol
¿Cómo haría eso usando Swift?
Leo Dabus
8

En general, el sistema debe manejar la creación de instancias del controlador de vista con un guión gráfico. Lo que desea es recorrer la jerarquía de viewController tomando una referencia a los self.window.rootViewControllercontroladores de vista en lugar de inicializarlos, que ya deberían inicializarse correctamente si ha configurado su guión gráfico correctamente.

Entonces, digamos que tu rootViewControlleres un UINavigationController y luego quieres enviar algo a su controlador de vista superior, lo harías así en tu AppDelegate didFinishLaunchingWithOptions:

UINavigationController *nav = (UINavigationController *) self.window.rootViewController;
MyViewController *myVC = (MyViewController *)nav.topViewController;
myVC.data = self.data;

En Swift si sería muy similar:

let nav = self.window.rootViewController as! UINavigationController;
let myVC = nav.topViewController as! MyViewController
myVc.data = self.data

Realmente no debería estar inicializando los controladores de vista usando los id. De guiones gráficos del delegado de la aplicación a menos que desee omitir la forma normal en que se carga el guión gráfico y cargar el guión gráfico usted mismo. Si tiene que inicializar escenas desde AppDelegate, lo más probable es que esté haciendo algo mal. Me refiero a que, por alguna razón, desea enviar datos a un controlador de vista en la parte inferior de la pila, AppDelegate no debería estar llegando a la pila del controlador de vista para establecer los datos. Eso no es asunto suyo. Su negocio es el rootViewController. ¡Deje que rootViewController maneje a sus propios hijos! Por lo tanto, si el sistema eliminara el proceso normal de carga del guión gráfico al eliminar referencias a él en el archivo info.plist, a lo sumo instanciaría el rootViewController usandoinstantiateViewControllerWithIdentifier:, y posiblemente su raíz si es un contenedor, como un UINavigationController. Lo que desea evitar es crear instancias de controladores de vista que ya hayan sido instanciados por el guión gráfico. Este es un problema que veo mucho. En resumen, no estoy de acuerdo con la respuesta aceptada. Es incorrecto a menos que los carteles signifiquen eliminar la carga del guión gráfico de la lista de información, ya que de lo contrario habrá cargado 2 guiones gráficos, lo que no tiene sentido. Probablemente no sea una pérdida de memoria porque el sistema inicializó la escena raíz y la asignó a la ventana, pero luego apareció y la instanciaron nuevamente y la asignaron nuevamente. ¡Tu aplicación ha tenido un mal comienzo!

smileBot
fuente
0
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Tutorial" bundle:nil];
    self.window.rootViewController = [storyboard instantiateInitialViewController];
Ofir Malachi
fuente