Por que o viewWillAppear não é chamado quando um aplicativo volta do segundo plano?

Estou escrevendo um aplicativo e preciso alterar a visualização se o usuário estiver olhando para o aplicativo enquanto fala ao telefone.

Eu implementei o seguinte método:

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

Mas não está sendo chamado quando o aplicativo retorna ao primeiro plano.

Eu sei que posso implementar:

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

mas eu não quero fazer isso. Eu prefiro colocar todas as informações do meu layout no método viewWillAppear:, e deixar que lidem com todos os cenários possíveis.

Eu até tentei chamar viewWillAppear: from applicationWillEnterForeground :, mas não consigo identificar qual é o controlador de view atual naquele ponto.

Alguém sabe a maneira correta de lidar com isso? Tenho certeza que estou perdendo uma solução óbvia.

    O método viewWillAppear deve ser tomado no contexto do que está acontecendo em seu próprio aplicativo, e não no contexto de seu aplicativo ser colocado em primeiro plano quando você voltar para ele a partir de outro aplicativo.

    Em outras palavras, se alguém olhar para outro aplicativo ou receber uma binding telefônica, voltará para seu aplicativo que estava em segundo plano, seu UIViewController, que já estava visível quando você deixou o aplicativo “não liga”, por assim dizer. no que lhe diz respeito, nunca desapareceu e ainda é visível – e, portanto, o viewWillAppear não é chamado.

    Eu recomendo não chamar o viewWillAppear sozinho – ele tem um significado específico que você não deve subverter! Uma refatoração que você pode fazer para obter o mesmo efeito pode ser a seguinte:

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

    Então você também aciona o doMyLayoutStuff partir da notificação apropriada:

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

    Não há nenhuma maneira fora da checkbox para dizer qual é o UIViewController ‘atual’ pelo caminho. Mas você pode encontrar maneiras de contornar isso, por exemplo, existem methods delegates de UINavigationController para descobrir quando um UIViewController é apresentado nele. Você poderia usar tal coisa para rastrear o mais recente UIViewController que foi apresentado.

    Atualizar

    Se você planeja UIs com as máscaras de auto-rediscagem apropriadas nos vários bits, às vezes você nem precisa lidar com o layout ‘manual’ de sua interface do usuário – ele só é tratado …

    Rápido

    Resposta curta

    Use um observador NotificationCenter vez de 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 } 

    Resposta longa

    Para descobrir quando um aplicativo volta do segundo plano, use um observador NotificationCenter vez de viewWillAppear . Aqui está um projeto de exemplo que mostra quais events acontecem quando. (Esta é uma adaptação desta resposta do 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") } } 

    Ao iniciar o aplicativo pela primeira vez, a ordem de saída é:

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

    Depois de apertar o botão home e trazer o aplicativo de volta para o primeiro plano, a ordem de saída é:

     will enter foreground did become active 

    Então, se você estava originalmente tentando usar viewWillAppear então UIApplicationWillEnterForeground é provavelmente o que você deseja.

    Nota

    A partir do iOS 9 e posterior, você não precisa remover o observador. A documentação afirma:

    Se seu aplicativo segmentar o iOS 9.0 e posterior ou o macOS 10.11 e posterior, não será necessário cancelar o registro de um observador no método dealloc .

    Use o Notification Center no método viewDidLoad: do seu ViewController para chamar um método e faça o que você deveria fazer no seu método viewWillAppear: Chamar viewWillAppear: diretamente não é uma boa opção.

     - (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: um dos methods mais confusos nos SDKs do iOS, na minha opinião, nunca é invocado em tal situação, ou seja, comutação de aplicativos. Esse método é invocado apenas de acordo com a relação entre a visão do controlador de exibição e a janela do aplicativo , ou seja, a mensagem é enviada para um controlador de exibição apenas se sua exibição aparecer na janela do aplicativo, não na canvas.

    Quando seu aplicativo entra em segundo plano, obviamente as visualizações mais altas da janela do aplicativo não são mais visíveis para o usuário. Na perspectiva da sua janela de aplicativo, no entanto, eles ainda são as visualizações mais altas e, portanto, não desapareceram da janela. Em vez disso, essas visualizações desapareceram porque a janela do aplicativo desapareceu. Eles não desapareceram porque desapareceram da janela.

    Portanto, quando o usuário volta para o seu aplicativo, eles obviamente parecem aparecer na canvas, porque a janela aparece novamente. Mas do ponto de vista da janela, eles não desapareceram de jeito nenhum. Portanto, os view controllers nunca recebem a mensagem viewWillAppear:animated .

    Apenas tentando torná-lo o mais fácil possível, veja o código abaixo:

     - (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"); }