320 votes

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

Je suis en train d'écrire une application et j'ai besoin de changer la vue si l'utilisateur regarde l'application tout en parlant au téléphone.

J'ai mis en œuvre 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 revient au premier plan.

Je sais que je peux la 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 de loin mettre toutes mes informations de mise en page dans la méthode viewWillAppear :, et la laisser gérer tous les scénarios possibles.

J'ai même essayé d'appeler viewWillAppear : à partir de applicationWillEnterForeground :, mais je n'arrive pas à déterminer quel est le contrôleur de vue actuel à ce moment-là.

Quelqu'un connaît-il la marche à suivre ? Je suis sûr que je passe à côté d'une solution évidente.

1 votes

Vous devez utiliser applicationWillEnterForeground: pour déterminer quand votre application est revenue à l'état actif.

0 votes

J'ai dit que c'est ce que j'essayais de faire dans ma question. Veuillez vous référer à ce qui précède. Pouvez-vous proposer un moyen de déterminer quel est le contrôleur de vue actuel à partir du délégué de l'application ?

0 votes

Vous pouvez utiliser isMemberOfClass o isKindOfClass en fonction de vos besoins.

256voto

Suragch Points 197

Swift

Réponse courte

Utiliser un NotificationCenter observateur 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 une fonction NotificationCenter observateur plutôt que viewWillAppear . Voici un exemple de projet qui montre quels événements se produisent à quel moment. (Il s'agit d'une adaptation de cette réponse en 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 et ramené l'application au premier plan, l'ordre de sortie est le suivant :

will enter foreground
did become active 

Ainsi, si vous vouliez à l'origine utiliser viewWillAppear puis UIApplication.willEnterForegroundNotification est probablement ce que vous voulez.

Note

À partir d'iOS 9, il n'est plus nécessaire de supprimer l'observateur. L'observateur la documentation États :

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

217voto

occulus Points 10906

La méthode viewWillAppear doit être pris dans le contexte de ce qui se passe dans votre propre application, et non dans le contexte de votre application placée au premier plan lorsque vous y revenez à partir d'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 auparavant en arrière-plan, votre UIViewController qui était déjà visible lorsque vous avez quitté votre application "s'en fiche" 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 le viewWillAppear Il a une signification précise qu'il ne faut pas détourner ! Un remaniement que vous pouvez faire pour obtenir le même effet pourrait être le suivant :

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

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

Dans ce cas, vous déclenchez également doMyLayoutStuff de la notification appropriée :

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

Il n'y a d'ailleurs aucun moyen de savoir quel est le UIViewController "courant". Mais vous pouvez trouver des moyens de contourner ce problème, par exemple, il existe des méthodes déléguées de UINavigationController pour savoir quand un UIViewController est présenté à l'intérieur. Vous pourriez utiliser une telle méthode pour suivre le dernier UIViewController qui a été présenté.

Mise à jour

Si vous mettez en page les interfaces utilisateur avec les masques d'autodimensionnement appropriés sur les différents éléments, vous n'avez parfois même pas besoin de vous occuper de la mise en page "manuelle" de votre interface utilisateur - elle est tout simplement prise en charge...

106 votes

Merci pour cette solution. J'ai ajouté l'observateur pour UIApplicationDidBecomeActiveNotification et cela fonctionne très bien.

2 votes

C'est certainement la bonne réponse. Toutefois, en réponse à la question "il n'y a pas de moyen simple de savoir quel est le UIViewController 'courant'", je pense que self.navigationController.topViewController le fournit effectivement, ou du moins celui qui se trouve en haut de la pile, qui serait le courant si ce code est exécuté sur le thread principal dans un contrôleur de vue. (Je peux me tromper, je n'ai pas beaucoup joué avec, mais cela semble fonctionner).

0 votes

appDelegate.rootViewController fonctionnera également, mais il risque de renvoyer un UINavigationController et ensuite vous aurez besoin de .topViewController comme le dit @MatthewFrederick.

142voto

Manju Points 1185

Utiliser le centre de notification dans l'application viewDidLoad: de votre ViewController pour appeler une méthode et, à partir de là, faire ce que vous étiez censé faire dans votre viewWillAppear: méthode. 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");
}

9 votes

Il pourrait être judicieux de supprimer l'observateur en dealloc alors.

2 votes

ViewDidLoad n'est pas la meilleure méthode pour ajouter un observateur, si c'est le cas, supprimer l'observateur dans viewDidUnload

0 votes

Quelle est la meilleure méthode pour ajouter un observateur à son propre compte ?

37voto

MHC Points 4483

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

Lorsque votre application passe en arrière-plan, il est évident que les vues supérieures de la fenêtre d'application ne sont plus visibles pour l'utilisateur. Cependant, du point de vue de votre fenêtre d'application, ces vues sont toujours les plus hautes et n'ont donc pas disparu de la fenêtre. Au contraire, ces vues ont disparu parce que la fenêtre d'application a disparu. Elles n'ont pas disparu parce qu'elles ont disparu de la fenêtre.

Par conséquent, lorsque l'utilisateur revient à votre application, il semble manifestement apparaître à l'écran, car la fenêtre s'affiche à nouveau. Mais du point de vue de la fenêtre, elles n'ont pas du tout disparu. Par conséquent, les contrôleurs de vue n'obtiennent jamais l'information viewWillAppear:animated message.

3 votes

De plus, -viewWillDisappear:animated : était un endroit pratique pour sauvegarder l'état car il est appelé à la sortie de l'application. Il n'est pas appelé lorsque l'application est en arrière-plan, cependant, et une application en arrière-plan peut être tuée sans avertissement.

7 votes

Une autre méthode vraiment mal nommée est viewDidUnload. On pourrait penser qu'il s'agit de l'inverse de viewDidLoad, mais non ; elle n'est appelée que lorsqu'il y a eu une situation de mémoire faible qui a entraîné le déchargement de la vue, et non pas à chaque fois que la vue est effectivement déchargée au moment de l'allocation.

1 votes

Je suis tout à fait d'accord avec @occulus. viewWillAppear a son excuse parce que le multitâche (en quelque sorte) n'existait pas, mais viewDidUnload aurait certainement pu avoir un meilleur nom.

8voto

aviran Points 2334

Swift 4,2 / 5

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground),
                                           name: Notification.Name.UIApplicationWillEnterForeground,
                                           object: nil)
}

@objc func willEnterForeground() {
   // do what's needed
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X