ViewDidAppear no se llama al abrir la aplicación desde el fondo

175

Tengo un controlador de vista en el que mi valor es 0 (etiqueta) y cuando abro ese controlador de vista desde otro ViewController, viewDidAppearconfiguré el valor 20 en la etiqueta. Funciona bien, pero cuando cierro mi aplicación y de nuevo abro la aplicación, pero el valor no cambia porque viewDidLoad, viewDidAppeary viewWillAppearnada se ha llamado. ¿Cómo puedo llamar cuando abro mi aplicación? ¿Tengo que hacer algo applicationDidBecomeActive?

Zohaib
fuente
Puede publicar una notificación local cuando la aplicación se active y agregar su controlador de vista como observador y actualizar los valores.
Adil Soomro

Respuestas:

314

Curioso por la secuencia exacta de eventos, instrumenté una aplicación de la siguiente manera: (@Zohaib, puede usar el código NSNotificationCenter a continuación para responder a su pregunta).

// AppDelegate.m

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"app will enter foreground");
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"app did become active");
}

// ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"view did load");

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}

- (void)appDidBecomeActive:(NSNotification *)notification {
    NSLog(@"did become active notification");
}

- (void)appWillEnterForeground:(NSNotification *)notification {
    NSLog(@"will enter foreground notification");
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"view will appear");
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"view did appear");
}

En el lanzamiento, la salida se ve así:

2013-04-07 09:31:06.505 myapp[15459:11303] view did load
2013-04-07 09:31:06.507 myapp[15459:11303] view will appear
2013-04-07 09:31:06.511 myapp[15459:11303] app did become active
2013-04-07 09:31:06.512 myapp[15459:11303] did become active notification
2013-04-07 09:31:06.517 myapp[15459:11303] view did appear

Ingrese el fondo y vuelva a ingresar al primer plano:

2013-04-07 09:32:05.923 myapp[15459:11303] app will enter foreground
2013-04-07 09:32:05.924 myapp[15459:11303] will enter foreground notification
2013-04-07 09:32:05.925 myapp[15459:11303] app did become active
2013-04-07 09:32:05.926 myapp[15459:11303] did become active notification
danh
fuente
1
Danh, asignaste UIApplicationWillEnterForegroundNotification a appDidEnterForeground :. ¿No es eso un poco engañoso? Observe "will" y "did". ¿Fue eso intencional?
Lubiluk
@Lubiluk: no intencional. Lo editaré Buena atrapada.
danh
44
Esta fue una respuesta muy útil. Hice una versión de Swift aquí .
Suragch
¡Demostración perfecta del modo de fondo!
Marcelo dos Santos
¿Cuál es la secuencia de eventos cuando toca dos veces el botón de inicio y cierra la aplicación?
Amjad Husseini
134

Usando Objective-C

Usted debe registrar una UIApplicationWillEnterForegroundNotificationen su ViewController's viewDidLoadmétodo y cada vez que la aplicación se vuelve de fondo se puede hacer lo que quiere hacer en el método registrado para la notificación. ViewController'S viewWillAppear o viewDidAppear no serán llamados cuando la aplicación se vuelve de fondo al primer plano.

-(void)viewDidLoad{

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doYourStuff)

  name:UIApplicationWillEnterForegroundNotification object:nil];
}

-(void)doYourStuff{

   // do whatever you want to do when app comes back from background.
}

No olvide anular el registro de la notificación para la que está registrado.

-(void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Tenga en cuenta que si registra su viewControllerpara UIApplicationDidBecomeActiveNotificationentonces, se llamaría a su método cada vez que su aplicación se active, no se recomienda registrarse viewControllerpara esta notificación.

Usando Swift

Para agregar observador puede usar el siguiente código

 override func viewDidLoad() {
    super.viewDidLoad()

     NotificationCenter.default.addObserver(self, selector: "doYourStuff", name: UIApplication.willEnterForegroundNotification, object: nil)
 }

 func doYourStuff(){
     // your code
 }

Para eliminar el observador, puede usar la función deinit de swift.

deinit {
    NotificationCenter.default.removeObserver(self)
}
nsgulliver
fuente
44
Sí, es :) a veces es difícil encontrar respuestas :)
nsgulliver
@nsgulliver ¿Tengo que invocar manualmente cancelar el registro de la notificación - (nulo) dealloc {[[NSNotificationCenter defaultCenter] removeObserver: self]; }. ¿La aplicación lo hará por mí?
ios
43

Versión Swift 3.0 ++

En su viewDidLoad, regístrese en el centro de notificaciones para escuchar esto abierto desde la acción de fondo

NotificationCenter.default.addObserver(self, selector:#selector(doSomething), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        

Luego agregue esta función y realice la acción necesaria

func doSomething(){
    //...
}

Finalmente, agregue esta función para limpiar el observador de notificaciones cuando se destruya su controlador de vista.

deinit {
    NotificationCenter.default.removeObserver(self)
}
Colmillo
fuente
Solución fácil y directa para manejar la notificación dentro del VC +1
Mo Zaatar
No puedo creer que esta agradable respuesta se pierda en tantas otras preguntas SO similares / duplicadas.
Hugo Allexis Cardona
11

Swift 4.2. versión

Regístrese en NotificationCenter viewDidLoadpara recibir notificaciones cuando la aplicación regrese del fondo

NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: UIApplication.willEnterForegroundNotification, object: nil)

Implemente el método que debería llamarse.

@objc private func doSomething() {
    // Do whatever you want, for example update your view.
}

Puede eliminar el observador una vez que ViewControllerse destruye. Esto solo se requiere debajo de iOS9 y macOS 10.11

deinit {
    NotificationCenter.default.removeObserver(self)
}
gebirgsbärbel
fuente
1
FYI, estoy bastante seguro de que ya no es necesario molestarse en retirar observadores en estos días ...
Fattie
3

Simplemente haga que su controlador de vista se registre para la UIApplicationWillEnterForegroundNotificationnotificación y reaccione en consecuencia.

andreagiavatto
fuente
¿Cómo voy a hacer eso? He llamado a mi viewController en applicationDidBecomeActive pero. se superpone viewController o está bien hacer eso?
Zohaib
2
No llame a su viewController en applicationDidBecomeActive (que de todos modos está mal porque se llama varias veces). Regístrese para viewDidLoadrecibir la notificación sugerida en @nsgulliver. Su viewDidAppeartambién llamar doYourStuffa configurar su etiqueta con el valor deseado.
andreagiavatto
3

Creo que registrarse para la UIApplicationWillEnterForegroundNotification es arriesgado, ya que puede terminar con más de un controlador reaccionando a esa notificación. Nada garantiza que estos controladores sigan siendo visibles cuando se reciba la notificación.

Esto es lo que hago: forzo la llamada viewDidAppear en el controlador activo directamente del método didBecomeActive del delegado de la aplicación:

Agregue el siguiente código a - (void)applicationDidBecomeActive:(UIApplication *)application

UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
    activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
Erwan
fuente
77
Se garantiza si el controlador cancela el registro (como debería) para UIApplicationWillEnterForegroundNotification en viewWillDisappear en lugar de en dealloc. Llamar viewDidAppear explícitamente me parece un truco, rompe la semántica (vista personal) y puede confundir a las personas (por experiencia).
joakim
3

intente agregar esto en AppDelegate applicationWillEnterForeground.

func applicationWillEnterForeground(_ application: UIApplication) {        
    // makes viewWillAppear run
    self.window?.rootViewController?.beginAppearanceTransition(true, animated: false)
    self.window?.rootViewController?.endAppearanceTransition()
}
richc
fuente
2

Según la documentación de Apple:

(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated;

Descripción: le
dice a un controlador secundario que su apariencia está a punto de cambiar. Si está implementando un controlador de contenedor personalizado, use este método para decirle al niño que sus vistas están a punto de aparecer o desaparecer . No invoque viewWillAppear:, viewWillDisappear:, viewDidAppear:, o viewDidDisappear:directamente .

(void)endAppearanceTransition;

Descripción:

Le dice a un controlador secundario que su apariencia ha cambiado. Si está implementando un controlador de contenedor personalizado, use este método para decirle al niño que la transición de vista se ha completado.

Código de muestra:

(void)applicationDidEnterBackground:(UIApplication *)application
{

    [self.window.rootViewController beginAppearanceTransition: NO animated: NO];  // I commented this line

    [self.window.rootViewController endAppearanceTransition]; // I commented this line

}

Pregunta: ¿Cómo lo solucioné?

Respuesta : Encontré este trozo de líneas en la aplicación. Estas líneas hicieron que mi aplicación no recibiera ninguna notificación de ViewWillAppear. Cuando comenté estas líneas está funcionando bien .

Lakshmi
fuente