Vista de iPhone WillAppear no dispara

116

He leído muchos mensajes acerca de las personas que tienen problemas con viewWillAppearcuando no se crea la jerarquía de vista simplemente correcto. Mi problema es que no puedo entender qué significa eso.

Si creo un RootViewControllery llamo addSubViewa ese controlador, esperaría que las vistas agregadas estén conectadas para viewWillAppeareventos.

¿Alguien tiene un ejemplo de una jerarquía de vista programática compleja que reciba viewWillAppeareventos con éxito en todos los niveles?

Estado de los documentos de Apple:

Advertencia: Si la vista que pertenece a un controlador de vista se agrega directamente a una jerarquía de vista, el controlador de vista no recibirá este mensaje. Si inserta o agrega una vista a la jerarquía de vistas y tiene un controlador de vista, debe enviar este mensaje directamente al controlador de vista asociado. Si no se envía el controlador de vista, este mensaje evitará que se muestre cualquier animación asociada.

El problema es que no describen cómo hacer esto. ¿Qué significa "directamente"? ¿Cómo se agrega "indirectamente" una vista?

Soy bastante nuevo en Cocoa y iPhone, por lo que sería bueno si hubiera ejemplos útiles de Apple además de la basura básica de Hello World.

chzk
fuente
Tuve este problema hasta que me di cuenta de que estaba entendiendo mal el uso previsto de las subclases de UIViewController en general. Mira esta pregunta. stackoverflow.com/questions/5691226/…
averydev
7
¡¡¡Cuidado !!! Ya no es cierto en iOS 5 !!! Las llamadas viewWillAppear y viewDidAppear automáticamente
Vilém Kurz

Respuestas:

55

Si utiliza un controlador de navegación y establece su delegado, los métodos de vista {Will, Did} {Appear, Disappear} no se invocan.

En su lugar, debe usar los métodos delegados del controlador de navegación:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
mmalc
fuente
2
No había configurado el delegado de mi controlador de navegación y aún no se llamaba al método. De todos modos, lo configuré y luego usé los métodos que mencionaste anteriormente. Gracias.
Dimitris
Estoy viendo lo mismo que Dimitris
jkp
7
Acabo de probar esto en iOS4 y iOS5: Esto NO es cierto: configurar el delegado de un navigationController y luego presionar una vista en él activará viewWillAppear: etc.
DaGaMs
Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} AND func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animado: Bool) {}
Naloiko
28

Me he encontrado con el mismo problema. Simplemente envíe un viewWillAppearmensaje a su controlador de vista antes de agregarlo como una subvista. (Hay un parámetro BOOL que le dice al controlador de vista si se está animando a aparecer o no).

[myViewController viewWillAppear:NO];

Mire RootViewController.m en el ejemplo de Metronome.

(De hecho, los proyectos de ejemplo de Apple me parecieron geniales. Hay MUCHO más que HelloWorld;)

lajos
fuente
3
En realidad, debería llamar a viewWillAppear después de agregarlo a la subvista. De lo contrario, IBOutlets / IBActions no se conectarán.
4thSpace
2
Sí, después. Subvista creada desde XIB, no se llamó a viewWillAppear. Lo llamo yo mismo y todo funciona bien.
JOM
¡Gracias! Esto fue exactamente para mí. Estaba agregando manualmente una subvista a través de [scrollView addSubview:controller.view];. Agregué la línea [controller viewWillAppear:NO];después y ¡voilá! Trabajado como un encanto.
Rob S.
Con toda probabilidad, esto se debe a que su UIViewController está controlando una vista que es una subvista de una vista controlada por otro UIViewController. Este no es el patrón de diseño previsto. Para obtener más explicaciones, consulte esta publicación. stackoverflow.com/questions/5691226/…
averydev
6
¡¡¡Cuidado !!! ¡Ya no es cierto en iOS 5! Llama a viewWillAppear y viewDidAppear automáticamente. Si lo llama manualmente, se llamará dos veces.
Vilém Kurz
18

Finalmente encontré una solución para esto ¡QUE FUNCIONA!

UINavigationControllerDelegate

Creo que la esencia es configurar el delegado de su control de navegación en el controlador de vista en el que se encuentra, e implementarlo UINavigationControllerDelegatey sus dos métodos. ¡Brillante! ¡Estoy tan emocionado de que finalmente encontré una solución!

Chris
fuente
¿Cómo asignar un rootviewcontroller como delegado para navigationcontroller?
Gargo
1
¡NO FUNCIONA! intente minimizar la aplicación y maximizarla
Vyachaslav Gerchicov
8

Solo tuve el mismo problema. En mi aplicación tengo 2 controladores de navegación y presionar el mismo controlador de vista en cada uno de ellos funcionó en un caso y no en el otro. Quiero decir que al presionar exactamente el mismo controlador de vista en el primero UINavigationController,viewWillAppear se llamó pero no cuando se presionó en el segundo controlador de navegación.

Entonces me encontré con esta publicación UINavigationController debería llamar a los métodos viewWillAppear / viewWillDisappear

Y me di cuenta de que mi segundo controlador de navegación se redefinió viewWillAppear. Analizar el código mostró que no estaba llamando

[super viewWillAppear:animated];

¡Lo agregué y funcionó!

La documentación dice:

Si anula este método, debe llamar a super en algún momento de su implementación.

Antoine
fuente
Lo mismo aqui. Echado a perder por no llamar super.
olivaresF
5

He estado usando un controlador de navegación. Cuando quiero descender a otro nivel de datos o mostrar mi vista personalizada, uso lo siguiente:

[self.navigationController pushViewController:<view> animated:<BOOL>];

Cuando hago esto, consigo que la viewWillAppearfunción se active. Supongo que esto califica como "indirecto" porque no estoy llamando al addSubViewmétodo real yo mismo. No sé si esto es 100% aplicable a su aplicación, ya que no puedo decir si está usando un controlador de navegación, pero tal vez proporcione una pista.

Josh Gagnon
fuente
5

Gracias iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearY ViewDidAppearno se llamará en un controlador de vista de la presentación en IOS 13 que utiliza una nueva presentación modal que no cubre toda la pantalla.

Los créditos van a Arek Holko . Realmente me salvó el día.

ingrese la descripción de la imagen aquí

BilalReffas
fuente
4

En primer lugar, la barra de pestañas debe estar en el nivel raíz, es decir, agregada a la ventana, como se indica en la documentación de Apple. Esta es la clave para un comportamiento correcto.

En segundo lugar, puede usar UITabBarDelegate/ UINavigationBarDelegatepara reenviar las notificaciones manualmente, pero descubrí que para que toda la jerarquía de llamadas de vista funcione correctamente, todo lo que tenía que hacer era llamar manualmente

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

y

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. solo UNA VEZ antes de configurar los controladores de vista en el controlador respectivo (justo después de la asignación). A partir de ese momento, llamó correctamente a estos métodos en sus controladores de vista secundarios.

Mi jerarquía es así:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

El simple hecho de llamar a los métodos mencionados en la pestaña / controlador de navegación por primera vez aseguró que TODOS los eventos se reenviaron correctamente. Me impidió tener que llamarlos manualmente desde UINavigationBarDelegate/ UITabBarControllerDelegatemétodos.

Nota al margen: Curiosamente, cuando no funcionó, el método privado

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

.. que puede ver en la pila de llamadas en una implementación en funcionamiento, normalmente llama al viewWill/Did.. métodos, pero no lo hizo hasta que realicé lo anterior (a pesar de que fue llamado).

Sin embargo, creo que es MUY importante que UITabBarControlleresté al nivel de la ventana y los documentos parecen respaldar esto.

Espero que haya sido claro (ish), feliz de responder más preguntas.

Sam
fuente
3

Como no se acepta respuesta y la gente (como yo) aterriza aquí, doy mi variación. Aunque no estoy seguro de que ese fuera el problema original. Cuando el controlador de navegación se agrega como una subvista a otra vista, debe llamar a los métodos viewWillAppear / Dissappear, etc. usted mismo de esta manera:

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [subNavCntlr viewWillAppear:animated];
}

- (void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [subNavCntlr viewWillDisappear:animated];
}

Solo para completar el ejemplo. Este código aparece en mi ViewController donde creé y agregué el controlador de navegación a una vista que coloqué en la vista.

- (void)viewDidLoad {

    // This is the root View Controller
    rootTable *rootTableController = [[rootTable alloc]
                 initWithStyle:UITableViewStyleGrouped];

    subNavCntlr = [[UINavigationController alloc]   
                  initWithRootViewController:rootTableController];

    [rootTableController release];

    subNavCntlr.view.frame = subNavContainer.bounds;

    [subNavContainer addSubview:subNavCntlr.view];

    [super viewDidLoad];
}

el .h se ve así

@interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
    IBOutlet UIView *subNavContainer;
    UINavigationController *subNavCntlr;
}

@end

En el archivo nib tengo la vista y debajo de esta vista tengo una etiqueta, una imagen y el contenedor (otra vista) donde coloco el controlador. Así es como se ve. Tuve que revolver algunas cosas ya que esto era trabajo para un cliente.

texto alternativo

hol
fuente
3

Las vistas se agregan "directamente" llamando [view addSubview:subview]. Las vistas se agregan "indirectamente" mediante métodos como barras de pestañas o barras de navegación que intercambian subvistas.

Cada vez que llame [view addSubview:subviewController.view], debe llamar [subviewController viewWillAppear:NO](o SÍ, según sea su caso).

Tuve este problema cuando implementé mi propio sistema de administración de vista raíz personalizado para una subpantalla en un juego. Agregar manualmente la llamada a viewWillAppear solucionó mi problema.

AndrewS
fuente
3

La forma correcta de hacer esto es usando la API de contención UIViewController.

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}
Hari Kunwar
fuente
Esta es absolutamente la solución moderna correcta (iOS 9,8,7). Además, si está cambiando el controlador de vista incorporado sobre la marcha, deberá llamar a [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke
1
En este caso, es viewWillAppear:posible que aún no se llame
Vyachaslav Gerchicov
2

Utilizo este código para controladores de vista push y pop:

empujar:

[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];

popular:

[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];

.. y funciona bien para mí.

Arash Zeinoddini
fuente
2

Un error muy común es el siguiente. Tiene un punto de vista, UIView* ay otro, UIView* b. Agrega b a a como una subvista. Si intenta llamar a viewWillAppear en b, nunca se disparará, porque es una subvista de un

Giuseppe
fuente
2

iOS 13 mordió mi aplicación en el trasero aquí. Si ha notado un cambio de comportamiento a partir de iOS 13, simplemente configure lo siguiente antes de presionarlo:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Es posible que también deba configurarlo en su .storyboard en el inspector de atributos (configure Presentación en Pantalla completa).

Esto hará que su aplicación se comporte como lo hacía en versiones anteriores de iOS.

dólar
fuente
1

No estoy 100% seguro de esto, pero creo que agregar una vista a la jerarquía de vista significa directamente llamar -addSubview:a la vista del controlador de vista (por ejemplo, [viewController.view addSubview:anotherViewController.view]) en lugar de empujar un nuevo controlador de vista a la pila del controlador de navegación.

Martin Gordon
fuente
1

Creo que agregar una subvista no significa necesariamente que la vista aparecerá, por lo que no hay una llamada automática al método de la clase que lo hará.


fuente
1

Creo que lo que quieren decir "directamente" es conectar las cosas de la misma manera que lo hace la plantilla "Aplicación de navegación" de xcode, que establece el UINavigationController como la única subvista de la UIWindow de la aplicación.

El uso de esa plantilla es la única forma en que he podido obtener los métodos Will / Did / Appear / Disappear llamados en el objeto ViewControllers al presionar / hacer estallar esos controladores en el UINavigationController. Ninguna de las otras soluciones en las respuestas aquí funcionó para mí, incluida la implementación en RootController y pasarlas al NavigationController (secundario). Esas funciones (aparecerán / aparecerán / desaparecerán) solo se llamaron en mi RootController al mostrar / ocultar los VC de nivel superior, mi "login" y los VC de navegación, no los sub-VC en el controlador de navegación, por lo que no tuve la oportunidad de "pasarlos" al Nav VC.

Terminé usando la funcionalidad de delegado de UINavigationController para buscar las transiciones particulares que requerían funcionalidad de seguimiento en mi aplicación, y eso funciona, pero requiere un poco más de trabajo para conseguir que tanto la funcionalidad de desaparecer como la de aparecer "simulen".

También es una cuestión de principios hacer que funcione después de golpearme la cabeza contra este problema durante horas hoy. Cualquier fragmento de código que funcione utilizando un RootController personalizado y un VC de navegación secundario sería muy apreciado.

Bogatyr
fuente
1

En caso de que esto ayude a alguien. Tuve un problema similar donde mi ViewWillAppearno está disparando en un UITableViewController. Después de jugar mucho, me di cuenta de que el problema era que el UINavigationControllerque está controlando mi UITableViewno está en la vista raíz. Una vez que solucione eso, ahora funciona como un campeón.

Andrés
fuente
¿Puedes compartir "cómo" hiciste eso?
Brabbeldas
1

Yo mismo tuve este problema y me llevó 3 horas completas (2 de las cuales busqué en Google) solucionarlo.

Lo que resultó ser de ayuda fue simplemente eliminar la aplicación del dispositivo / simulador, limpiar y luego ejecutar nuevamente .

Espero que ayude

Finn Gaida
fuente
1
[self.navigationController setDelegate:self];

Establezca el delegado en el controlador de vista raíz.

Gaurav
fuente
1

Para Swift. Primero cree el protocolo para llamar a lo que quería llamar en viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Segundo, crea la clase

class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
    if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
        updatedCntllr.myViewWillAppear()
    }
}

}

En tercer lugar, haga que la instancia de ForceUpdateOnViewAppear sea el miembro de la clase adecuada que tenga acceso al controlador de navegación y exista mientras exista el controlador de navegación. Puede ser, por ejemplo, el controlador de vista raíz del controlador de navegación o la clase que lo crea o lo presenta. Luego asigne la instancia de ForceUpdateOnViewAppear a la propiedad de delegado del controlador de navegación lo antes posible.

Vadim Motorine
fuente
0

En mi caso, el problema fue con la animación de transición personalizada. Cuando se establecemodalPresentationStyle = .custom viewWillAppear no se llama

en la clase de animación de transición personalizada se necesitan métodos de llamada: beginAppearanceTransitionyendAppearanceTransition

ober
fuente
0

En mi caso, eso fue solo un error extraño en el emulador de ios 12.1. Desapareció después de lanzarse en un dispositivo real.

abrazo frío
fuente
0

He creado una clase que resuelve este problema. Simplemente configúrelo como un delegado de su controlador de navegación e implemente uno o dos métodos simples en su controlador de vista, que se llamarán cuando la vista esté a punto de mostrarse o se haya mostrado a través de NavigationController

Aquí está el GIST que muestra el código

GregJaskiewicz
fuente
0

ViewWillAppear es un método de anulación de la clase UIViewController, por lo que agregar un subView no llamará a viewWillAppear, pero cuando presente, presione, muestre, muestre, establezca o haga popToRootViewController desde un viewController, luego se llamará a viewWillAppear para el viewController presentado.

Abu Ul Hassan
fuente
0

Mi problema era que no se llamaba a viewWillAppear al desconectar de una transición. La respuesta fue poner una llamada a viewWillAppear (verdadero) en la secuencia de desenrollado en el Controlador de vista al que regresa

@IBAction func desenrollar (para desenrollarSegue: UIStoryboardSegue, ViewController laterVC: Cualquiera) {

   viewWillAppear(true)
}
usuario2385491
fuente
-2

No estoy seguro de que sea el mismo problema que resolví.
En algunas ocasiones, el método no se ejecuta de forma normal como "[self methodOne]".

Tratar

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
Sean
fuente
el problema es viewWillAppearque no se llama en absoluto
Vyachaslav Gerchicov
-3

Solo debe tener 1 UIViewController activo en cualquier momento. Cualquier subvista que desee manipular debe ser exactamente eso, subVIEWS, es decir, UIView.

Utilizo una técnica simple para administrar mi jerarquía de vistas y aún no he encontrado un problema desde que comencé a hacer las cosas de esta manera. Hay 2 puntos clave:

  • se debe usar un único UIViewController para administrar "el valor de una pantalla" de su aplicación
  • use UINavigationController para cambiar vistas

¿Qué quiero decir con "el valor de una pantalla"? Es un poco vago a propósito, pero generalmente es una característica o sección de su aplicación. Si tiene algunas pantallas con la misma imagen de fondo pero diferentes superposiciones / ventanas emergentes, etc., debería ser 1 controlador de vista y varias vistas secundarias. Nunca debería encontrarse trabajando con 2 controladores de vista. Tenga en cuenta que aún puede crear una instancia de una UIView en un controlador de vista y agregarla como una subvista de otro controlador de vista si desea que ciertas áreas de la pantalla se muestren en varios controladores de vista.

En cuanto a UINavigationController, ¡este es tu mejor amigo! Apague la barra de navegación y especifique NO para animación, y tendrá una excelente manera de cambiar de pantalla a pedido. Puede empujar y desplegar controladores de vista si están en una jerarquía, o puede preparar una matriz de controladores de vista (incluida una matriz que contenga un solo VC) y configurarla para que sea la pila de vistas usando setViewControllers. Esto le brinda total libertad para cambiar VC, mientras obtiene todas las ventajas de trabajar dentro del modelo esperado de Apple y hacer que todos los eventos, etc., se activen correctamente.

Esto es lo que hago cada vez que inicio una aplicación:

  • comenzar desde una aplicación basada en ventanas
  • agregue un UINavigationController como rootViewController de la ventana
  • agregue lo que quiera que sea mi primer UIViewController como rootViewController del controlador de navegación

(tenga en cuenta que comenzar desde una ventana es solo una preferencia personal; me gusta construir cosas yo mismo, así que sé exactamente cómo se construyen. Debería funcionar bien con la plantilla basada en vistas)

Todos los eventos se disparan correctamente y básicamente la vida es buena. Luego, puede dedicar todo su tiempo a escribir las partes importantes de su aplicación y no perder el tiempo tratando de piratear manualmente las jerarquías de vistas para darle forma.

Nigel Flack
fuente
No hay nada de malo en tener varios controladores de vista activos; UIViewController tiene métodos para permitir la construcción de una jerarquía (por ejemplo, [addChildViewController:]).
Richard
1
Sí, lo hace ahora. En 2011 no fue así. La respuesta era precisa en ese momento, pero ahora no lo es.
Nigel Flack