Esta línea:
[self dismissViewControllerAnimated:YES completion:nil];
no se está enviando un mensaje a sí mismo, en realidad está enviando un mensaje a su VC presentador, pidiéndole que haga el rechazo. Cuando presenta un VC, crea una relación entre el VC que presenta y el presentado. Por lo tanto, no debe destruir el VC que se presenta mientras se está presentando (el VC presentado no puede devolver ese mensaje de descarte ...). Como realmente no lo está tomando en cuenta, está dejando la aplicación en un estado confuso. Vea mi respuesta Descartar un controlador de vista presentado
en el que recomiendo que este método esté escrito más claramente:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
En su caso, debe asegurarse de que todo el control se realice en formato mainVC
. Debe usar un delegado para enviar el mensaje correcto a MainViewController desde ViewController1, para que mainVC pueda descartar VC1 y luego presentar VC2.
En VC2 VC1 agregue un protocolo en su archivo .h arriba de @interface:
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
y más abajo en el mismo archivo en la sección @interface declare una propiedad para contener el puntero delegado:
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
En el archivo .m VC1, el método del botón de descarte debe llamar al método delegado
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
Ahora en mainVC, configúrelo como delegado de VC1 al crear VC1:
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
e implementar el método delegado:
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2:
puede ser el mismo método que el VC2Pressed:
método IBAction del botón. Tenga en cuenta que se llama desde el bloque de finalización para garantizar que VC2 no se presente hasta que VC1 se descarte por completo.
Ahora se está moviendo de VC1-> VCMain-> VC2, por lo que probablemente querrá que solo una de las transiciones esté animada.
actualizar
En sus comentarios expresa sorpresa por la complejidad necesaria para lograr algo aparentemente simple. Le aseguro que este patrón de delegación es tan fundamental para gran parte de Objective-C y Cocoa, y este ejemplo es el más simple que puede obtener, que realmente debería hacer el esfuerzo para sentirse cómodo con él.
En la Guía de programación del controlador de vista de Apple, tienen esto que decir :
Descartar un controlador de vista presentado
Cuando llega el momento de descartar un controlador de vista presentado, el enfoque preferido es dejar que el controlador de vista de presentación lo descarte. En otras palabras, siempre que sea posible, el mismo controlador de vista que presentó el controlador de vista también debe asumir la responsabilidad de descartarlo. Aunque existen varias técnicas para notificar al controlador de vista de presentación que su controlador de vista presentada debe descartarse, la técnica preferida es la delegación. Para obtener más información, consulte "Uso de la delegación para comunicarse con otros controladores".
Si realmente piensa en lo que quiere lograr y cómo lo está haciendo, se dará cuenta de que enviar mensajes a su MainViewController para que haga todo el trabajo es la única salida lógica dado que no desea utilizar un NavigationController. Si se hace uso de un NavController, en efecto, son 'delegar', aunque no de forma explícita, a la NavController que hacer todo el trabajo. Debe haber algún objeto que mantenga un seguimiento central de lo que sucede con la navegación de su VC, y necesita algún método para comunicarse con él, hagas lo que hagas.
En la práctica, el consejo de Apple es un poco extremo ... en casos normales, no es necesario crear un delegado y un método dedicados, puede confiar en [self presentingViewController] dismissViewControllerAnimated:
; es cuando, en casos como el suyo, desea que su despido tenga otros efectos en el control remoto. objetos que debes cuidar.
Aquí hay algo que podría imaginar para trabajar sin todas las molestias de los delegados ...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
Después de pedirle al controlador de presentación que nos descarte, tenemos un bloque de finalización que llama a un método en el controlador de presentación de visualización para invocar VC2. No se necesita delegado. (Un gran punto de venta de los bloques es que reducen la necesidad de delegados en estas circunstancias). Sin embargo, en este caso hay algunas cosas que se interponen en el camino ...
- en VC1 no sabes que mainVC implementa el método
present2
; puede terminar con errores o fallas difíciles de depurar. Los delegados le ayudan a evitar esto.
- una vez que se descarta VC1, no está realmente disponible para ejecutar el bloque de finalización ... ¿o no? ¿Self.presentingViewController significa algo más? No sabes (yo tampoco) ... con un delegado, no tienes esta incertidumbre.
- Cuando intento ejecutar este método, simplemente se bloquea sin advertencias ni errores.
Así que por favor ... ¡tómate el tiempo para aprender a delegar!
actualización2
En su comentario, ha logrado que funcione usando esto en el controlador de botón de descarte de VC2:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Sin duda, esto es mucho más simple, pero te deja con una serie de problemas.
Acoplamiento
estrecho Está conectando la estructura de su viewController. Por ejemplo, si insertara un nuevo viewController antes de mainVC, su comportamiento requerido se interrumpiría (navegaría al anterior). En VC1 también ha tenido que #importar VC2. Por lo tanto, tiene bastantes interdependencias, lo que rompe los objetivos de OOP / MVC.
Al usar delegados, ni VC1 ni VC2 necesitan saber nada sobre mainVC o sus antecedentes, por lo que mantenemos todo acoplado libremente y modular.
La memoria
VC1 no se ha ido, todavía tiene dos indicadores:
presentedViewController
propiedad de mainVC
presentingViewController
Propiedad de VC2
Puede probar esto iniciando sesión, y también simplemente haciendo esto desde VC2
[self dismissViewControllerAnimated:YES completion:nil];
Todavía funciona, todavía te lleva de vuelta a VC1.
Eso me parece una pérdida de memoria.
La pista de esto está en la advertencia que está recibiendo aquí:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
La lógica se rompe, ya que intenta descartar el VC de presentación del cual VC2 es el VC presentado. El segundo mensaje no se ejecuta realmente; bueno, tal vez sucedan algunas cosas, pero aún te quedan dos punteros a un objeto del que pensabas que te habías deshecho. ( editar - He comprobado esto y no es tan malo, ambos objetos desaparecen cuando regresas a mainVC )
Esa es una forma bastante prolija de decir: por favor, use delegados. Si ayuda, hice otra breve descripción del patrón aquí:
¿Pasar un controlador en un constructor siempre es una mala práctica?
actualización 3
Si realmente desea evitar a los delegados, esta podría ser la mejor salida:
En VC1:
[self presentViewController:VC2
animated:YES
completion:nil];
Pero no descarte nada ... como comprobamos, en realidad no sucede de todos modos.
En VC2:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
Como (sabemos) no hemos descartado VC1, podemos volver a través de VC1 a MainVC. MainVC descarta VC1. Debido a que VC1 se ha ido, se presenta que VC2 lo acompaña, por lo que está de regreso en MainVC en un estado limpio.
Todavía está muy acoplado, ya que VC1 necesita saber sobre VC2, y VC2 necesita saber que se llegó a través de MainVC-> VC1, pero es lo mejor que obtendrá sin un poco de delegación explícita.