Creando un segue programáticamente

202

Tengo un campo común UIViewControllerque UIViewsControllersextiendo para reutilizar algunas operaciones comunes.

Quiero configurar un segue en este "Común" UIViewControllerpara que todos los demás UIViewControllershereden.

Estoy tratando de descubrir cómo hago eso programáticamente.

Supongo que la pregunta también podría ser cómo configuro un seguepara todos mis UIViewControllerssin entrar en el guión gráfico y hacerlo a mano.

Tiago Veloso
fuente

Respuestas:

169

Por definición, un segue realmente no puede existir independientemente de un guión gráfico. Es aún existe en el nombre de la clase: UIStoryboardSegue. No crea segues mediante programación: es el tiempo de ejecución del guión gráfico lo que los crea para usted. Normalmente puede llamar performSegueWithIdentifier:al código de su controlador de vista, pero esto se basa en tener un segue ya configurado en el guión gráfico para hacer referencia.

Sin embargo, lo que creo que está preguntando es cómo puede crear un método en su controlador de vista común (clase base) que pasará a un nuevo controlador de vista y será heredado por todas las clases derivadas. Puede hacer esto creando un método como este en su controlador de vista de clase base:

- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

y luego en su clase derivada, llame a ese método cuando se haga clic en el botón apropiado o se seleccione la fila de la tabla o lo que sea.

jonkroll
fuente
44
Gracias. Es una pena que no podamos hacerlo mediante programación. Realmente aumentaría la calidad del código fuente (menos duplicación siempre es buena). Iré con tu sugerencia.
Tiago Veloso
2
@jonkroll ¿es posible llamar / realizar segue de la declaración de cambio, es decir, según el índice que tengo?
codejunkie
55
@codejunkie: Sí, puedes hacer eso. Usaría el UIViewControllermétodo nombrado performSegueWithIdentifier:sender:para esto.
jonkroll
2
He estado creando y realizando segue programáticamente (vea mi respuesta). ¿Algo está mal con mi código, entonces, si su respuesta es correcta?
Jean-Philippe Pellet
14
Actualización para iOS 6+: UIView's presentModalViewController:animated:está en desuso. De los documentos - (Desaprobado en iOS 6.0. Utilice presentViewController: animado: finalización: en su lugar.)
usuario
346

Pensé que agregaría otra posibilidad. Una de las cosas que puede hacer es conectar dos escenas en un guión gráfico utilizando un segue que no está conectado a una acción, y luego desencadenar programáticamente el segue dentro de su controlador de vista. La forma en que hace esto es que tiene que arrastrar desde el icono del propietario del archivo en la parte inferior de la escena del guión gráfico que es la escena de segmentación y arrastrar hacia la escena de destino. Pondré una imagen para ayudar a explicar.

ingrese la descripción de la imagen aquí

Aparecerá una ventana emergente para "Manual Segue". Elegí Push como el tipo. Toca el pequeño cuadrado y asegúrate de estar en el inspector de atributos. Déle un identificador que usará para referirse a él en el código.

ingrese la descripción de la imagen aquí

Ok, a continuación voy a seguir usando un elemento de botón de barra programática. En viewDidLoad o en otro lugar, crearé un elemento de botón en la barra de navegación con este código:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

Ok, observe que el selector es buttonizeButtonTap :. Entonces escriba un método nulo para ese botón y dentro de ese método llamará al segue de esta manera:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

El parámetro del remitente es necesario para identificar el botón cuando se llama a prepareForSegue. prepareForSegue es el método de marco en el que creará una instancia de su escena y le transmitirá los valores que necesite para hacer su trabajo. Así es como se ve mi método:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

Ok, solo lo probé y funciona. Espero que te ayude.

smileBot
fuente
@MichaelRowe ¿cómo elimina esto la necesidad de segues? Como lo veo, todavía tienes que arrastrar y soltar en Storyboard en el controlador de destino ..
aherrick
@MichaelRowe esto no elimina la necesidad de segues. Lo que esto hace es permitirle seguir entre los controladores de vista que están integrados en el código en lugar de en el generador de interfaces.
Mateo
@ Matt, en realidad solo me vuelvo a pensar completamente cómo configuro mi aplicación ... Después de una reescritura completa de toda la interfaz de usuario, ya no uso ningún segue ..
Michael Rowe
@cocoanut recibo el error como "La aplicación intentó presentar modalmente un controlador activo" cualquier ayuda con respecto a esto ..
Bala
1
Manual Segue "Push" está en desuso, use "Mostrar". Esta respuesta tiene más detalles. @smileBot, actualice la respuesta.
NAbbas
81

He estado usando este código para crear una instancia de mi subclase de segue personalizada y ejecutarla mediante programación. Parece funcionar. Algo mal con esto? Estoy perplejo, leyendo todas las otras respuestas diciendo que no se puede hacer.

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
Jean-Philippe Pellet
fuente
44
¿Qué hay en MyCustomSegue?
Victor Engel
3
Es una subclase personalizada de UIStoryboardSegue.
Jean-Philippe Pellet
77
@ MarkAmery Mucha gente (incluyéndome a mí) evita usar guiones gráficos. Son difíciles de fusionar, y no hay verificación en tiempo de compilación de que la ID a la que estoy pasando performSegueWithIdentifier:esté realmente definida en el guión gráfico. Evito todos los problemas si creo el segue yo mismo.
Jean-Philippe Pellet
3
Estoy de acuerdo con Jean-Philippe. Administrar el guión gráfico es un dolor en el trasero. Por supuesto, es fácil hacer clic para crear pocas vistas y agregar un segmento aquí y otro allí, pero administrar 6 vistas con 16 segmentos definidos en XML, cuando tienes tres desarrolladores, todos jugando con ellos es terrible. De todos modos, el punto es: el código te da control, xml generado por xcode no.
Krystian
3
Veo un bloqueo en [segue perform] en iOS7, no estoy seguro si alguien más está experimentando esto.
Eric Chen
45

Supongo que esto es respondido y aceptado, pero solo me gustaría agregarle algunos detalles más.

Lo que hice para resolver un problema en el que presentaba una vista de inicio de sesión como primera pantalla y luego quería pasar a la aplicación si el inicio de sesión era correcto. Creé el segmento desde el controlador de vista de inicio de sesión al controlador de vista raíz y le di un identificador como "myidentifier".

Luego, después de verificar todo el código de inicio de sesión, si el inicio de sesión era correcto, llamaría

[self performSegueWithIdentifier: @"myidentifier" sender: self];

Mi mayor malentendido fue que intenté poner el segue en un botón e interrumpir el segue una vez que lo encontré.

qrikko
fuente
44
Como escribí como otro comentario: he estado creando y realizando segues personalizados mediante programación (vea mi respuesta).
Jean-Philippe Pellet
32

Tienes que vincular tu código a la UIStoryboard que está utilizando. Asegúrese de ir a YourViewController en su UIStoryboard, haga clic en el borde que lo rodea y luego configure su identifiercampo en el NSStringque llame en su código.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
Jeff Grimes
fuente
1
Entiendo esto, pero ¿qué pasa si el viewController que quiero presentar está incrustado en un NavigationController en el guión gráfico? Por lo que puedo encontrar, puedo inicializar un NavigationController para incrustarlo, pero en el guión gráfico, ya tengo una configuración de segmentación de inserción para la vista que debe presentarse.
jhilgert00
1
¿Puedes dar más detalles sobre esto? Creo que este es el problema que estoy teniendo, pero parece que no puedo encontrar cómo / dónde hacer esto ...
jesses.co.tt
1
Incluso esta solución es correcta, se trata de evitar cualquier tipo de segue, pero la pregunta se trata de segue. De esta manera, puede conectarse o hacer una transición entre dos escenas SIN segue en los guiones gráficos.
BootMaker
16

Para los controladores que están en el guión gráfico.

jhilgert00 ¿es esto lo que estabas buscando?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

O...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];
usuario1723341
fuente
3

Me gustaría agregar una aclaración ...

Un malentendido común, de hecho uno que tuve durante algún tiempo, es que el prepareForSegue:sender:método desencadena una secuencia de guiones gráficos . No lo es. Se realizará un seguimiento del guión gráfico, independientemente de si ha implementado un prepareForSegue:sender:método para ese controlador de vista (partiendo de).

Aprendí esto de las excelentes conferencias de iTunesU de Paul Hegarty . Mis disculpas, pero lamentablemente no puedo recordar qué conferencia.

Si conecta un segmento entre dos controladores de vista en un guión gráfico, pero no implementa un prepareForSegue:sender: método, el segue seguirá en el controlador de vista de destino. Sin embargo, pasará a ese controlador de vista sin preparación.

Espero que esto ayude.

andrewbuilder
fuente
3

Los segmentos del guión gráfico no se deben crear fuera del guión gráfico. Tendrá que cablearlo, a pesar de los inconvenientes.

La referencia de UIStoryboardSegue establece claramente:

No crea objetos de segue directamente. En cambio, el tiempo de ejecución del guión gráfico los crea cuando debe realizar una transición entre dos controladores de vista. Aún puede iniciar un segue programáticamente utilizando el método performSegueWithIdentifier: sender: UIViewController si lo desea. Puede hacerlo para iniciar un seguimiento desde una fuente que se agregó mediante programación y, por lo tanto, no está disponible en Interface Builder.

Todavía puede decirle programáticamente al guión gráfico que presente un controlador de vista usando un segue usando presentModalViewController:o pushViewController:animated:llamadas, pero necesitará una instancia de guión gráfico.

Puede llamar UIStoryboardal método de clase s para obtener un guión gráfico con un paquete nulo para el paquete principal.

storyboardWithName:bundle:

Cameron Lowell Palmer
fuente
2

Primero, suponga que tiene dos vistas diferentes en el guión gráfico y desea navegar de una pantalla a otra, así que siga estos pasos:

1) Defina todas sus vistas con el archivo de clase y también la identificación del guión gráfico en el inspector de identidad.

2) Asegúrese de agregar un controlador de navegación a la primera vista. Selecciónelo en el Guión gráfico y luego Editor> Incrustar en> Controlador de navegación

3) En su primera clase, importe el "secondClass.h"

#import "ViewController.h
#import "secondController.h"

4) Agregue este comando en la IBAction que debe realizar la segue

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5) @"second"es la clase de controlador de segunda vista, id del guión gráfico.

Sanket Chauhan
fuente
self.storyboarddebería ser:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat
@masipcat y el nombre del Story Board podrían depender de cómo configuró su proyecto Xcode, en el mío era "Main.storyboard", así que lo uséstoryboardWithName:@"Main"
ammianus
@ sanket-chauhan si su primer controlador no está incrustado en un Controlador de navegación, también puede mostrar la siguiente vista usando [self showDetailViewController:next sender:self];o[self showViewController:next sender:self];
ammianus
1

Realicé ingeniería inversa e hice una (re) implementación de código abierto de los segmentos de UIStoryboard: https://github.com/acoomans/Segway

Con esa biblioteca, puede definir segues mediante programación (sin ningún guión gráfico).

Espero que pueda ayudar.

acoomans
fuente
0

Un par de problemas, en realidad:

Primero, en ese proyecto que cargó para nosotros, el segue no tiene el identificador "segue1":

sin identificador

Debe completar ese identificador si aún no lo ha hecho.

En segundo lugar, a medida que avanza de la vista de tabla a la vista de tabla, llama a initWithNibName para crear un controlador de vista. Realmente quieres usar instantiateViewControllerWithIdentifier.

Jaydip
fuente
0

Aquí está el ejemplo de código para Creating a segue programmatically:

class ViewController: UIViewController {
    ...
    // 1. Define the Segue
    private var commonSegue: UIStoryboardSegue!
    ...
    override func viewDidLoad() {
        ...
        // 2. Initialize the Segue
        self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
            self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
        }
        ...
    }
    ...
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 4. Prepare to perform the Segue
        if self.commonSegue == segue {
            ...
        }
        ...
    }
    ...
    func actionFunction() {
        // 3. Perform the Segue
        self.prepare(for: self.commonSegue, sender: self)
        self.commonSegue.perform()
    }
    ...
}
jqgsninimo
fuente
Estás llamando self.prepare(for: self.commonSegue, sender: self)desde tu método de acción. Entonces, ¿cuál es el punto de comparar if self.commonSegue == segue {...}en el prepare(for:sender)método?
nayem
@nayem: en prepare(for:sender:), puede configurar el controlador de vista de destino antes de que se muestre. Por supuesto, también puedes hacerlo actionFunction.
jqgsninimo
@nayem: La razón por la que hago esto es para tratar de ser coherente con el manejo de otros segmentos.
jqgsninimo