ViewDidAppear não é chamado ao abrir o aplicativo de segundo plano

175

Eu tenho um View Controller no qual meu valor é 0 (label) e quando abro esse View Controller de outro ViewController, configurei viewDidAppearpara definir o valor 20 no label. Funciona bem, mas quando fecho o aplicativo e, novamente, abro o aplicativo, mas o valor não muda porque viewDidLoad, viewDidAppeare viewWillAppearnada é chamado. Como posso ligar quando abro meu aplicativo. Eu tenho que fazer alguma coisaapplicationDidBecomeActive ?

Zohaib
fonte
Você pode postar uma notificação local quando o aplicativo se tornar ativo e adicionar seu controlador de exibição como observador e atualizar valores.
Adil Soomro 07/04

Respostas:

314

Curioso sobre a sequência exata de eventos, instrumentei um aplicativo da seguinte maneira: (@Zohaib, você pode usar o código NSNotificationCenter abaixo para responder sua pergunta).

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

No lançamento, a saída fica assim:

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

Digite o plano de fundo e digite novamente o primeiro 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
fonte
1
Danh, você mapeou UIApplicationWillEnterForegroundNotification para appDidEnterForeground :. Isso não é um pouco enganador? Observe "will" e "did". Isso foi intencional?
Lubiluk
@Lubiluk - não intencional. Eu vou editar. Boa pegada.
Danh
4
Esta foi uma resposta muito útil. Eu fiz uma versão rápida aqui .
Suragch
Demonstração perfeita do modo de fundo!
Marcelo dos Santos
Qual é a sequência de eventos quando você clica duas vezes no botão home e fecha o aplicativo?
Amjad Husseini
134

Usando Objective-C

Você deve registrar um UIApplicationWillEnterForegroundNotificationna sua ViewController's viewDidLoadmétodo e sempre aplicativo vem de volta do fundo você pode fazer o que quer fazer no método registrado para notificação. ViewControllerO viewWillAppear ou o viewDidAppear não serão chamados quando o aplicativo voltar do segundo plano para o primeiro 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.
}

Não se esqueça de cancelar o registro da notificação para a qual você está registrado.

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

Nota se você registrar o seu viewControllerpara UIApplicationDidBecomeActiveNotification, em seguida, o seu método seria chamado cada vez que seu aplicativo se torna ativo, não é recomendado para registrar viewControllerpara esta notificação.

Usando Swift

Para adicionar observador, você pode usar o seguinte código

 override func viewDidLoad() {
    super.viewDidLoad()

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

 func doYourStuff(){
     // your code
 }

Para remover o observador, você pode usar a função deinit de swift.

deinit {
    NotificationCenter.default.removeObserver(self)
}
nsgulliver
fonte
4
Sim, é :) às vezes é difícil encontrar respostas :)
nsgulliver
@nsgulliver Preciso chamar manualmente para cancelar o registro da notificação - (void) dealloc {[[NSNotificationCenter defaultCenter] removeObserver: self]; } O aplicativo fará isso por mim?
ios
43

Versão Swift 3.0 ++

No seu viewDidLoad, registre-se na central de notificações para ouvir esta ação aberta em segundo plano

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

Em seguida, adicione esta função e execute a ação necessária

func doSomething(){
    //...
}

Por fim, adicione esta função para limpar o observador de notificações quando o seu controlador de exibição for destruído.

deinit {
    NotificationCenter.default.removeObserver(self)
}
Fangming
fonte
Fácil e solução para a frente para lidar com a notificação dentro do VC +1
Mo Zaatar
Não posso acreditar que essa resposta legal seja perdida em muitas outras questões SO / duplicadas semelhantes.
Hugo Allexis Cardona
11

Swift 4.2. versão

Registre-se no NotificationCenter viewDidLoadpara ser notificado quando o aplicativo retornar do segundo plano

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

Implemente o método que deve ser chamado.

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

Você pode remover o observador depois que ele ViewControllerfor destruído. Isso é necessário apenas abaixo do iOS9 e macOS 10.11

deinit {
    NotificationCenter.default.removeObserver(self)
}
gebirgsbärbel
fonte
1
FYI eu tenho certeza que você não precisa se preocupar remover mais observadores nos dias de hoje ...
Fattie
3

Basta fazer com que seu controlador de visualização se registre para a UIApplicationWillEnterForegroundNotificationnotificação e reaja de acordo.

andreagiavatto
fonte
Como vou fazer isso? Eu chamei meu viewController em applicationDidBecomeActive but. se sobrepõem ao viewController ou é bom fazer isso?
Zohaib
2
Não chame seu viewController em applicationDidBecomeActive (que está errado de qualquer maneira porque é chamado várias vezes). Registre-se para viewDidLoadreceber a notificação como sugerido por @nsgulliver. Você viewDidAppeartambém ligará doYourStuffpara definir seu rótulo com o valor desejado.
precisa saber é o seguinte
3

Acho que registrar-se no UIApplicationWillEnterForegroundNotification é arriscado, pois você pode acabar com mais de um controlador reagindo a essa notificação. Nada garante que esses controladores ainda estejam visíveis quando a notificação for recebida.

Aqui está o que eu faço: Forço a chamada viewDidAppear no controlador ativo diretamente do método didBecomeActive do delegado do aplicativo:

Adicione o código abaixo a - (void)applicationDidBecomeActive:(UIApplication *)application

UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
    activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
Erwan
fonte
7
É garantido se o controlador cancelar o registro (como deveria) para o UIApplicationWillEnterForegroundNotification em viewWillDisappear em vez de em desalocação. Chamar viewDidAppear parece explicitamente um hack para mim, quebra a semântica (visão pessoal) e pode confundir as pessoas (por experiência).
joakim
3

tente adicionar isso no aplicativo AppDelegate applicationWillEnterForeground.

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

De acordo com a documentação da Apple:

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

Descrição:
informa a um controlador filho que sua aparência está prestes a mudar. Se você estiver implementando um controlador de contêiner personalizado, use este método para informar à criança que suas exibições estão prestes a aparecer ou desaparecer . Não invocar viewWillAppear:, viewWillDisappear:, viewDidAppear:, ou viewDidDisappear:diretamente .

(void)endAppearanceTransition;

Descrição:

Diz a um controlador filho que sua aparência mudou. Se você estiver implementando um controlador de contêiner personalizado, use este método para informar ao filho que a transição da exibição está concluída.

Código de amostra:

(void)applicationDidEnterBackground:(UIApplication *)application
{

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

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

}

Pergunta: Como consertei?

Resp : Eu encontrei este pedaço de linhas em aplicação. Essas linhas fizeram com que meu aplicativo não recebesse nenhuma notificação do ViewWillAppear. Quando comentei essas linhas, está funcionando bem .

Lakshmi
fonte