life container ios uiviewcontroller

ios - container - viewcontroller swift



Detectar cuando se despide un controlador de vista presentado (12)

Digamos, tengo una instancia de una clase de controlador de vista llamada VC2. En VC2, hay un botón "cancelar" que se descartará solo. Pero no puedo detectar ni recibir ninguna devolución de llamada cuando el botón "cancelar" se activó. VC2 es una caja negra.

Un controlador de vista (llamado VC1) presentará VC2 utilizando el presentViewController:animated:completion:

¿Qué opciones tiene VC1 para detectar cuándo se despidió VC2?

Edit: Del comentario de @rory mckinnel y la respuesta de @NicolasMiari, intenté lo siguiente:

En VC2:

-(void)cancelButton:(id)sender { [self dismissViewControllerAnimated:YES completion:^{ }]; // [super dismissViewControllerAnimated:YES completion:^{ // // }]; }

En VC1:

//-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^ _Nullable)(void))completion { NSLog(@"%s ", __PRETTY_FUNCTION__); [super dismissViewControllerAnimated:flag completion:completion]; // [self dismissViewControllerAnimated:YES completion:^{ // // }]; }

Pero no se llamaba a la dismissViewControllerAnimated en el VC1.


  1. Cree un archivo de clase (.h / .m) y asígnele el nombre: DismissSegue
  2. Seleccione Subclase de: UIStoryboardSegue

  3. Vaya al archivo DismissSegue.m y escriba el siguiente código:

    - (void)perform { UIViewController *sourceViewController = self.sourceViewController; [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; }

  4. Abra el guión gráfico y luego presione Ctrl + arrastre desde el botón de cancelar a VC1 y seleccione Acción Segue como Descartar y listo.


@ user523234 - "Pero no se llamaba a la clase shutViewControllerAnimated en el VC1".

No se puede suponer que VC1 hace realmente la presentación; podría ser el controlador de vista raíz, VC0, por ejemplo. Hay 3 controladores de vista involucrados:

  • sourceViewController
  • presentandoViewController
  • presentadoViewController

En su ejemplo, VC1 = sourceViewController , VC2 = presentedViewController VC1 = sourceViewController , ?? = presentingViewController ?? = presentingViewController - tal vez VC1, tal vez no.

Sin embargo, siempre puede confiar en que se llame a VC1.animationControllerForDismissedController (si ha implementado los métodos de delegado) al descartar VC2 y en ese método puede hacer lo que quiera con VC1


Como se ha mencionado, la solución es usar la override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) .

Para aquellos que se preguntan por qué la override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) no siempre funciona, es posible que un UINavigationController esté interceptando la UINavigationController si se está gestionando. Escribí una subclase que debería ayudar:

class DismissingNavigationController: UINavigationController { override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { super.dismiss(animated: flag, completion: completion) topViewController?.dismiss(animated: flag, completion: completion) } }



Puede usar el desvío segue para realizar esta tarea, sin necesidad de usar el exitModalViewController. Defina un método de segue de desenrollado en su VC1.

Vea este enlace sobre cómo crear el segmento de desenrollado, https://.com/a/15839298/5647055 .

Suponiendo que su segue de desenrollado esté configurado, en el método de acción definido para su botón "Cancelar", puede realizar el segue como:

[self performSegueWithIdentifier:@"YourUnwindSegueName" sender:nil];

Ahora, cada vez que presione el botón "Cancelar" en el VC2, se cerrará y aparecerá VC1. También llamará al método de desenrollado, definido en VC1. Ahora, usted sabe cuándo se despide el controlador de vista presentado.


Según los documentos, el controlador de presentación es responsable del despido real. Cuando el controlador presentado se retire, pedirá al presentador que lo haga por él. Por lo tanto, si invalida el método descartarViewControllerAnimated en su controlador VC1, creo que se activará cuando presione cancelar en VC2. Detecta el descarte y luego llama a la versión de las súper clases que hará el despido real.

Como se descubrió en la discusión, esto no parece funcionar. En lugar de confiar en el mecanismo subyacente, en lugar de llamar a dismissViewControllerAnimated:completion de dismissViewControllerAnimated:completion de dismissViewControllerAnimated:completion en VC2 en sí mismo, llame a la dismissViewControllerAnimated:completion de self.presentingViewController de self.presentingViewController de self.presentingViewController dismissViewControllerAnimated:completion en self.presentingViewController en VC2. Esto llamará directamente a su reemplazo.

Un mejor enfoque sería que VC2 proporcione un bloque al que se llama cuando el controlador modal se ha completado.

Así que en VC2, proporcione una propiedad de bloque, por ejemplo, con el nombre onDoneBlock .

En VC1 presentas lo siguiente:

  • En VC1, crea VC2

  • Establezca el controlador hecho para VC2 como: VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};

  • Presente el controlador VC2 normalmente usando [self presentViewController: VC2 animated: YES complete: nil];

  • En VC2, en la acción de cancelación de destino, llamar a self.onDoneBlock();

El resultado es que VC2 le dice a quien lo levanta que está hecho. Puede extender el onDoneBlock para tener argumentos que indiquen si el modal se completó, canceló, tuvo éxito, etc.


Si anula en el controlador de vista que está siendo atenuado:

override func removeFromParentViewController() { super.removeFromParentViewController() // your code here }

Al menos esto funcionó para mí.


Tanto el controlador de vista de presentación como el de presentación pueden llamar a dismissViewController:animated: para descartar el controlador de vista presentado.

La primera opción es (posiblemente) la "correcta", en cuanto a diseño: el mismo controlador de vista "principal" es responsable de presentar y descartar el controlador de vista modal ("secundario").

Sin embargo, este último es más conveniente: por lo general, el botón "descartar" se adjunta a la vista del controlador de vista presentado, y tiene dicho controlador de vista establecido como su objetivo de acción.

Si está adoptando el enfoque anterior, ya conoce la línea de código en el controlador de la vista de presentación donde se produce el despido: ejecute el código justo después de que se dismissViewControllerAnimated:completion: o dentro del bloque de finalización.

Si está adoptando este último enfoque (el controlador de vista presentado se despide a sí mismo), tenga en cuenta que al llamar a dismissViewControllerAnimated:completion: desde el controlador de vista presentado, UIKit llama a ese método en el controlador de vista de presentación

Discusión

El controlador de vista de presentación es responsable de descartar el controlador de vista que presentó. Si llama a este método en el controlador de vista presentado, UIKit solicita al controlador de vista de presentación que se encargue de la cancelación.

( fuente: referencia de clase UIViewController )

Por lo tanto, para interceptar tal evento, podría anular ese método en el controlador de vista de presentación :

override func dismissViewControllerAnimated(_ flag: Bool, completion completion: (() -> Void)?) { super.dismissViewControllerAnimated(flag, completion:completion) // Your custom code here... }


Usa una propiedad de bloque

Declarar en VC2

var onDoneBlock : ((Bool) -> Void)?

Configuración en VC1

VC2.onDoneBlock = { result in // Do something }

Llame a VC2 cuando esté a punto de despedirse

onDoneBlock!(true)


Uso lo siguiente para indicar a un coordinador que el controlador de vista está "listo". Esto se usa en una subclase AVPlayerViewController en una aplicación de tvOS y se llamará después de que la transición de despido de playerVC haya finalizado:

class PlayerViewController: AVPlayerViewController { var onDismissal: (() -> Void)? override func beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) { super.beginAppearanceTransition(isAppearing, animated: animated) transitionCoordinator?.animate(alongsideTransition: nil, completion: { [weak self] _ in if !isAppearing { self?.onDismissal?() } }) } }


override ing viewDidAppear hizo el truco por mí. Utilicé un Singleton en mi modal y ahora puedo configurar y obtener de eso dentro del VC que llama, el modal y en cualquier otro lugar.


viewWillDisappear función viewWillDisappear en el controlador de vista presentado.

override func viewWillDisappear(_ animated: Bool) { //Your code here }