web-dev-qa-db-fra.com

Pourquoi viewWillAppear n'est-il pas appelé lorsqu'une application revient de l'arrière-plan?

J'écris une application et je dois changer la vue si l'utilisateur regarde l'application tout en parlant au téléphone.

J'ai implémenté la méthode suivante:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear:");
    _sv.frame = CGRectMake(0.0, 0.0, 320.0, self.view.bounds.size.height);
}

Mais l'appel n'est pas appelé lorsque l'application revient au premier plan.

Je sais que je peux implémenter:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];

mais je ne veux pas faire ça. Je préfère de loin mettre toutes mes informations de mise en page dans la méthode viewWillAppear: et laisser cela gérer tous les scénarios possibles.

J'ai même essayé d'appeler viewWillAppear: depuis applicationWillEnterForeground:, mais je n'arrive pas à identifier le contrôleur de vue actuel à ce stade.

Est-ce que quelqu'un sait comment s'y prendre? Je suis sûr qu'il me manque une solution évidente.

265
Philip Walton

La méthode viewWillAppear doit être prise dans le contexte de ce qui se passe dans votre propre application et non dans le contexte où votre application est placée au premier plan lorsque vous y revenez depuis une autre application.

En d'autres termes, si quelqu'un regarde une autre application ou prend un appel téléphonique, puis revient à votre application qui était précédemment en arrière-plan, votre UIViewController qui était déjà visible lorsque vous avez quitté votre application "ne s'en soucie pas pour ainsi dire - en ce qui le concerne, il n'a jamais disparu et il est toujours visible - et donc viewWillAppear n'est pas appelé.

Je recommande de ne pas appeler vous-même le viewWillAppear - il a une signification spécifique à ne pas subvertir! Une refactorisation que vous pouvez faire pour obtenir le même effet pourrait être la suivante:

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

- (void)doMyLayoutStuff:(id)sender {
    // stuff
}

Ensuite, vous déclenchez également doMyLayoutStuff à partir de la notification appropriée:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:self];

Au fait, il n’existe aucun moyen de déterminer quel est le "courant" UIViewController "actuel". Mais vous pouvez trouver des moyens de contourner cela, par exemple Il existe des méthodes de délégation de UINavigationController pour savoir quand un UIViewController y est présenté. Vous pouvez utiliser une telle chose pour suivre le dernier UIViewController qui a été présenté.

Mise à jour

Si vous mettez en forme des interfaces utilisateur avec les masques autorésensibles appropriés sur les différents bits, vous n'avez parfois même pas besoin de vous occuper de la présentation "manuelle" de votre interface utilisateur - elle est simplement traitée ...

192
occulus

Swift

Réponse courte

Utilisez un observateur NotificationCenter plutôt que viewWillAppear.

override func viewDidLoad() {
    super.viewDidLoad()

    // set observer for UIApplication.willEnterForegroundNotification
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

}

// my selector that was defined above
@objc func willEnterForeground() {
    // do stuff
}

Réponse longue

Pour savoir quand une application revient de l'arrière-plan, utilisez un observateur NotificationCenter plutôt que viewWillAppear. Voici un exemple de projet qui montre quels événements se produisent quand. (Ceci est une adaptation de cette réponse Objective-C .)

import UIKit
class ViewController: UIViewController {

    // MARK: - Overrides

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")

        // add notification observers
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

    }

    override func viewWillAppear(_ animated: Bool) {
        print("view will appear")
    }

    override func viewDidAppear(_ animated: Bool) {
        print("view did appear")
    }

    // MARK: - Notification oberserver methods

    @objc func didBecomeActive() {
        print("did become active")
    }

    @objc func willEnterForeground() {
        print("will enter foreground")
    }

}

Au premier démarrage de l'application, l'ordre de sortie est le suivant:

view did load
view will appear
did become active
view did appear

Après avoir appuyé sur le bouton d'accueil, puis ramené l'application au premier plan, l'ordre de sortie est le suivant:

will enter foreground
did become active 

Donc si vous vouliez utiliser viewWillAppear alors UIApplication.willEnterForegroundNotification est probablement ce que vous voulez.

Remarque

À partir de iOS 9 et ultérieur, vous n'avez pas besoin de supprimer l'observateur. Le documentation indique:

Si votre application cible iOS 9.0 et versions ultérieures ou macOS 10.11 et versions ultérieures, vous n'avez pas besoin de désenregistrer un observateur dans sa méthode dealloc.

178
Suragch

Utilisez le Centre de notifications de la méthode viewDidLoad: de votre ViewController pour appeler une méthode et, à partir de là, faites ce que vous étiez censé faire dans votre méthode viewWillAppear:. Appeler directement viewWillAppear: n'est pas une bonne option.

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

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationIsActive:) 
        name:UIApplicationDidBecomeActiveNotification 
        object:nil];

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

- (void)applicationIsActive:(NSNotification *)notification {
    NSLog(@"Application Did Become Active");
}

- (void)applicationEnteredForeground:(NSNotification *)notification {
    NSLog(@"Application Entered Foreground");
}
138
Manju

viewWillAppear:animated:, l'une des méthodes les plus déroutantes du SDK iOS, à mon avis, n'est jamais invoqué dans une telle situation, c'est-à-dire le changement d'application. Cette méthode est uniquement appelée en fonction de la relation entre la vue du contrôleur de vue et la fenêtre de l'application, autrement dit, le message est envoyé à un contrôleur de vue uniquement si sa vue apparaît dans la fenêtre de l'application, et non à l'écran. .

Lorsque votre application passe en arrière-plan, les plus hautes vues de la fenêtre de l'application ne sont plus visibles pour l'utilisateur. Dans la perspective de la fenêtre de votre application, cependant, ce sont toujours les vues les plus hautes et, par conséquent, elles ne sont pas disparues de la fenêtre. Au contraire, ces vues ont disparu parce que la fenêtre de l'application a disparu. Ils n'ont pas disparu car ils ont disparu de de la fenêtre.

Par conséquent, lorsque l'utilisateur revient à votre application, ils semblent évidemment apparaître à l'écran, car la fenêtre réapparaît. Mais du point de vue de la fenêtre, ils n'ont pas du tout disparu. Par conséquent, les contrôleurs de vue ne reçoivent jamais le message viewWillAppear:animated.

33
MHC

J'essaie juste de le rendre aussi facile que possible, voir le code ci-dessous:

- (void)viewDidLoad
{
   [self appWillEnterForeground]; //register For Application Will enterForeground
}


- (id)appWillEnterForeground{ //Application will enter foreground.

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(allFunctions)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    return self;
}


-(void) allFunctions{ //call any functions that need to be run when application will enter foreground 
    NSLog(@"calling all functions...application just came back from foreground");


}
3
ConfusedDeer