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

J’écris une application et je dois modifier 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 il n’est pas appelé lorsque l’application retourne au premier plan.

Je sais que je peux mettre en œuvre:

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

mais je ne veux pas faire ça. Je préfère plutôt 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: from applicationWillEnterForeground :, mais je n’arrive pas à identifier le contrôleur de vue actuel à ce stade.

Quelqu’un connaît-il la bonne façon de gérer cela? Je suis sûr qu’il me manque une solution évidente.

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 se 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 vous déconseille d’appeler la viewWillAppear vous-même – elle a une signification spécifique que vous ne devriez pas subvertir! Un refactoring que vous pouvez faire pour obtenir le même effet peut être le suivant:

 - (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]; 

Il n’y a pas moyen de dire qui est le UIViewController ‘actuel’. Mais vous pouvez trouver des moyens de contourner ce problème, par exemple, il existe des méthodes de délégation de UINavigationController pour savoir quand un UIViewController y est présenté. Vous pourriez utiliser une telle chose pour suivre le dernier UIViewController qui a été présenté.

Mettre à jour

Si vous placez des interfaces utilisateur avec les masques de ré-autorisage appropriés sur les différents bits, vous n’avez parfois même pas besoin de vous occuper de la disposition «manuelle» de votre interface utilisateur.

Rapide

Réponse courte

Utilisez un observateur NotificationCenter plutôt que viewWillAppear .

 override func viewDidLoad() { super.viewDidLoad() // set observer for UIApplicationWillEnterForeground NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil) } // my selector that was defined above @objc func willEnterForeground() { // do stuff } 

Longue réponse

Pour savoir quand une application revient en 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: NSNotification.Name.UIApplicationDidBecomeActive, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: NSNotification.Name.UIApplicationWillEnterForeground, 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 viewWillAppear à l’origine d’utiliser viewWillAppear UIApplicationWillEnterForeground est probablement ce que vous voulez.

Remarque

A partir d’iOS 9 et versions ultérieures, vous n’avez pas besoin de supprimer l’observateur. La 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ésinscrire un observateur dans sa méthode dealloc .

Utilisez Notification Center dans la méthode viewDidLoad: de votre ViewController pour appeler une méthode et à partir de là, faites ce que vous deviez faire dans votre méthode viewWillAppear: Appeler viewWillAppear: directement 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"); } 

viewWillAppear:animated: à mon avis, l’une des méthodes les plus déroutantes dans les SDK iOS n’est jamais invoquée dans une telle situation, à savoir le changement d’application. Cette méthode est uniquement invoquée en fonction de la relation entre la vue du contrôleur de vue et la fenêtre de l’application , c’est-à-dire que 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 vues les plus hautes de la fenêtre de l’application ne sont évidemment plus visibles pour l’utilisateur. Dans la perspective de votre fenêtre d’application, cependant, elles restnt les vues les plus hautes et ne disparaissent donc pas de la fenêtre. Ces vues ont plutôt disparu car la fenêtre de l’application a disparu. Ils n’ont pas disparu parce qu’ils ont disparu de la fenêtre.

Par conséquent, lorsque l’utilisateur retourne à votre application, il semble que celles-ci apparaissent à l’écran, car la fenêtre réapparaît. Mais du sharepoint 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 .

Essayez simplement de le rendre aussi simple que possible, voyez 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"); }