iPhone viewWillAppear não dispara


116

Eu li vários posts sobre pessoas que têm problemas viewWillAppearquando você não cria sua hierarquia de visualização da maneira certa. Meu problema é que não consigo descobrir o que isso significa.

Se eu criar um RootViewControllere chamar addSubViewesse controlador, espero que as visualizações adicionadas sejam conectadas para viewWillAppeareventos.

Alguém tem um exemplo de hierarquia de visualização programática complexa que recebe viewWillAppeareventos em todos os níveis com sucesso ?

Estado do Docs da Apple:

Aviso: Se a visualização pertencente a um controlador de visualização for adicionada diretamente a uma hierarquia de visualização, o controlador de visualização não receberá esta mensagem. Se você inserir ou adicionar uma visualização à hierarquia de visualização e ela tiver um controlador de visualização, deverá enviar esta mensagem diretamente ao controlador de visualização associado. Deixar de enviar esta mensagem ao controlador de visualização impedirá que qualquer animação associada seja exibida.

O problema é que eles não descrevem como fazer isso. O que significa "diretamente"? Como você adiciona uma visualização "indiretamente"?

Eu sou bastante novo no Cocoa e no iPhone, então seria bom se houvesse exemplos úteis da Apple além da porcaria básica do Hello World.


Tive esse problema até perceber que não entendia bem o uso pretendido das subclasses de UIViewController em geral. Confira esta questão. stackoverflow.com/questions/5691226/…
averydev

7
Cuidado !!! Não é mais verdade no iOS 5 !!! Chama viewWillAppear e viewDidAppear automatically
Vilém Kurz

Respostas:


55

Se você usar um controlador de navegação e definir seu delegado, os métodos de visualização {Will, Did} {Appear, Disappear} não são chamados.

Em vez disso, você precisa usar os métodos de delegado do controlador de navegação:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:

2
Eu não tinha definido o delegado do meu controlador de navegação e ainda assim o método não estava sendo chamado. Enfim, eu configurei e então usei os métodos que você mencionou acima. Obrigado.
Dimitris

Estou vendo a mesma coisa que Dimitris
jkp

7
Acabei de testar isso no iOS4 e iOS5: Isso NÃO é verdade: Definir o delegado de um navigationController e, em seguida, enviar uma visualização para ele IRÁ disparar viewWillAppear: etc.
DaGaMs

Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} AND func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animado: Bool
Niko

28

Eu tive esse mesmo problema. Basta enviar uma viewWillAppearmensagem para seu controlador de visualização antes de adicioná-la como uma subvisão. (Há um parâmetro BOOL que diz ao controlador de visualização se ele está sendo animado para aparecer ou não.)

[myViewController viewWillAppear:NO];

Observe RootViewController.m no exemplo do Metronome.

(Na verdade, achei os projetos de exemplo da Apple ótimos. Há MUITO mais do que HelloWorld;)


3
Na verdade, você deve chamar viewWillAppear depois de adicioná-lo à subvisualização. Caso contrário, IBOutlets / IBActions não serão conectados.
4º Espaço

2
Sim, depois. Subvisualização criada a partir do XIB, viewWillAppear não foi chamado. Ligue sozinho e tudo funcionará bem.
JOM

Obrigado! Foi exatamente isso para mim. Eu estava adicionando manualmente uma subvisualização via [scrollView addSubview:controller.view];. Eu adicionei a linha [controller viewWillAppear:NO];depois e voila! Funcionou como um encanto.
Rob S.

Com toda a probabilidade, isso ocorre porque seu UIViewController está controlando uma visualização que é uma subvisualização de uma visualização controlada por outro UIViewController. Este não é o padrão de design pretendido. Para mais explicações, confira este post. stackoverflow.com/questions/5691226/…
averydev

6
Cuidado !!! Não é mais verdade no iOS 5 !!! Chama viewWillAppear e viewDidAppear automaticamente. Se você o chamasse manualmente, seria chamado duas vezes.
Vilém Kurz

18

Finalmente encontrei uma solução PARA ISSO QUE FUNCIONA!

UINavigationControllerDelegate

Eu acho que a essência disso é definir o delegado do controle de navegação para o viewcontroller em que ele está e implementar UINavigationControllerDelegatee seus dois métodos. Brilhante! Estou tão animado que finalmente encontrei uma solução!


como atribuir um rootviewcontroller como delegado para o controle de navegação?
Gargo

1
NÃO FUNCIONA! tente minimizar o aplicativo e
maximizá-

8

Eu simplesmente tive o mesmo problema. Em meu aplicativo, tenho 2 controladores de navegação e empurrar o mesmo controlador de visualização em cada um deles funcionou em um caso e não no outro. Quero dizer que ao empurrar exatamente o mesmo controlador de visualização no primeiro UINavigationController, viewWillAppearfoi chamado, mas não quando empurrado no segundo controlador de navegação.

Então me deparei com este post UINavigationController deve chamar os métodos viewWillAppear / viewWillDisappear

E percebi que meu segundo controlador de navegação redefiniu viewWillAppear. A triagem do código mostrou que eu não estava ligando

[super viewWillAppear:animated];

Eu adicionei e funcionou!

A documentação diz:

Se você substituir esse método, deverá chamar super em algum ponto de sua implementação.


Mesma coisa aqui. Confuso por não ligar para super.
olivaresF

5

Estou usando um controlador de navegação. Quando desejo descer para outro nível de dados ou mostrar minha visualização personalizada, uso o seguinte:

[self.navigationController pushViewController:<view> animated:<BOOL>];

Quando faço isso, faço a viewWillAppearfunção disparar. Suponho que isso se qualifique como "indireto" porque não estou chamando o addSubViewmétodo real . Não sei se isso é 100% aplicável ao seu aplicativo, pois não posso dizer se você está usando um controlador de navegação, mas talvez forneça uma pista.


5

Obrigado iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearE ViewDidAppearnão será chamado em um controlador de vista apresentando no iOS 13 que utiliza uma nova apresentação modal que não cobre toda a tela.

Os créditos vão para Arek Holko . Ele realmente salvou meu dia.

insira a descrição da imagem aqui


4

Em primeiro lugar, a barra de guias deve estar no nível da raiz, ou seja, adicionada à janela, conforme declarado na documentação da Apple. Esta é a chave para um comportamento correto.

Em segundo lugar, você pode usar UITabBarDelegate/ UINavigationBarDelegatepara encaminhar as notificações manualmente, mas descobri que para fazer com que toda a hierarquia de chamadas de visualização funcione corretamente, tudo que eu tenho que fazer é chamar manualmente

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

e

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. apenas UMA VEZ antes de configurar os controladores de visualização no respectivo controlador (logo após a alocação). A partir de então, ele chamou corretamente esses métodos em seus controladores de visualização filho.

Minha hierarquia é assim:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

Apenas chamar os métodos mencionados no controlador tab / nav pela primeira vez garantiu que TODOS os eventos fossem encaminhados corretamente. Isso me impediu de precisar chamá-los manualmente a partir dos métodos UINavigationBarDelegate/ UITabBarControllerDelegate.

Sidenote: Curiosamente, quando não funcionou, o método privado

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

.. que você pode ver na pilha de chamadas em uma implementação funcional, geralmente chama os viewWill/Did..métodos, mas não o fazia até que eu executei o procedimento acima (embora tenha sido chamado).

Eu acho que é MUITO importante que o UITabBarControlleresteja no nível da janela e os documentos parecem confirmar isso.

Espero que tenha ficado claro (ish), fico feliz em responder a outras perguntas.


3

Como nenhuma resposta é aceita e as pessoas (como eu) pousam aqui, dou minha variação. Embora eu não tenha certeza de que esse seja o problema original. Quando o controlador de navegação é adicionado como uma subvisão a uma outra visão, você deve chamar os métodos viewWillAppear / Dissappear etc.

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

    [subNavCntlr viewWillAppear:animated];
}

- (void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [subNavCntlr viewWillDisappear:animated];
}

Apenas para tornar o exemplo completo. Este código aparece em meu ViewController onde criei e adicionei o controlador de navegação em uma visão que coloquei na visão.

- (void)viewDidLoad {

    // This is the root View Controller
    rootTable *rootTableController = [[rootTable alloc]
                 initWithStyle:UITableViewStyleGrouped];

    subNavCntlr = [[UINavigationController alloc]   
                  initWithRootViewController:rootTableController];

    [rootTableController release];

    subNavCntlr.view.frame = subNavContainer.bounds;

    [subNavContainer addSubview:subNavCntlr.view];

    [super viewDidLoad];
}

o .h se parece com isso

@interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
    IBOutlet UIView *subNavContainer;
    UINavigationController *subNavCntlr;
}

@end

No arquivo nib eu tenho a visão e abaixo dessa visão tenho um rótulo de imagem e o contêiner (outra visão) onde coloquei o controlador. Aqui está como fica. Tive que embaralhar algumas coisas porque isso era trabalho para um cliente.

texto alternativo


3

As visualizações são adicionadas "diretamente" chamando [view addSubview:subview]. As visualizações são adicionadas "indiretamente" por métodos como barras de guias ou barras de navegação que trocam subvisualizações.

A qualquer momento que você ligar [view addSubview:subviewController.view], você deve ligar [subviewController viewWillAppear:NO](ou SIM, conforme o seu caso).

Tive esse problema quando implementei meu próprio sistema de gerenciamento de exibição de raiz personalizado para uma subtela de um jogo. Adicionar manualmente a chamada para viewWillAppear curou meu problema.


3

A maneira correta de fazer isso é usando a API de contenção UIViewController.

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}

Esta é a solução moderna absolutamente correta (iOS 9,8,7). Além disso, se você estiver mudando o controlador de visualização incorporado imediatamente, precisará chamar [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke,

1
Neste caso viewWillAppear:ainda não pode ser chamado
Vyachaslav Gerchicov

2

Eu uso este código para controladores push e pop view:

empurrar:

[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];

pop:

[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];

.. e funciona bem para mim.


2

Um erro muito comum é o seguinte. Você tem uma visualização UIView* a, e outra UIView* b,. Você adiciona b a a como uma subvisualização. Se você tentar chamar viewWillAppear em b, ele nunca será disparado, porque é uma subvisão de um


2

iOS 13 mordeu meu app na bunda aqui. Se você notou mudança de comportamento a partir do iOS 13, apenas defina o seguinte antes de empurrá-lo:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Você também pode precisar defini-lo em seu .storyboard no inspetor de Atributos (defina Apresentação como Tela Cheia).

Isso fará com que seu aplicativo se comporte como nas versões anteriores do iOS.


1

Não estou 100% certo disso, mas acho que adicionar uma visualização à hierarquia de visualização diretamente significa chamar -addSubview:a visualização do controlador de visualização (por exemplo, [viewController.view addSubview:anotherViewController.view]) em vez de colocar um novo controlador de visualização na pilha do controlador de navegação.


1

Acho que adicionar uma subvisualização não significa necessariamente que a visão aparecerá, então não há uma chamada automática para o método da classe


1

Acho que o que eles querem dizer é "diretamente" conectando as coisas da mesma maneira que o modelo xcode "Navigation Application" faz, que define o UINavigationController como a única subvisualização da UIWindow do aplicativo.

Usar esse modelo é a única maneira de obter os métodos Will / Did / Appear / Disappear chamados no objeto ViewControllers após push / pops desses controladores no UINavigationController. Nenhuma das outras soluções nas respostas aqui funcionou para mim, incluindo implementá-las no RootController e passá-las para o NavigationController (filho). Essas funções (irá / fez / aparecer / desaparecer) só foram chamadas em meu RootController ao mostrar / ocultar os VCs de nível superior, meu "login" e NavigationVCs, não os sub-VCs no controlador de navegação, então não tive oportunidade de "passe-os" para o Nav VC.

Acabei usando a funcionalidade de delegado do UINavigationController para procurar as transições específicas que exigiam a funcionalidade de acompanhamento em meu aplicativo, e isso funciona, mas requer um pouco mais de trabalho para obter a funcionalidade de desaparecer e aparecer "simulada".

Também é uma questão de princípio fazê-lo funcionar depois de bater minha cabeça contra este problema por horas hoje. Qualquer trecho de código funcional usando um RootController personalizado e um VC de navegação filho seria muito apreciado.


1

Caso isso ajude alguém. Eu tive um problema semelhante em que o meu ViewWillAppearnão estava atirando em a UITableViewController. Depois de muito brincar, percebi que o problema era que o UINavigationControllerque está controlando o meu UITableViewnão está na visualização do root. Depois de corrigir isso, ele agora está funcionando como um campeão.


Você pode compartilhar "como" você fez isso?
Brabbeldas

1

Eu mesmo tive esse problema e demorei 3 horas (2 das quais pesquisando no Google) para corrigi-lo.

O que acabou ajudando foi simplesmente deletar o aplicativo do dispositivo / simulador, limpar e rodar novamente .

espero que ajude


1
[self.navigationController setDelegate:self];

Defina o delegado para o controlador de visualização raiz.


1

Para Swift. Primeiro crie o protocolo para chamar o que você deseja chamar em viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Segundo, crie a classe

class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
    if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
        updatedCntllr.myViewWillAppear()
    }
}

}

Terceiro, faça com que a instância de ForceUpdateOnViewAppear seja o membro da classe apropriada que tem acesso ao Controlador de navegação e existe enquanto existir o controlador de navegação. Pode ser, por exemplo, o controlador de visualização raiz do controlador de navegação ou a classe que o cria ou apresenta. Em seguida, atribua a instância de ForceUpdateOnViewAppear à propriedade de delegado do Controlador de navegação o mais cedo possível.


0

No meu caso, o problema era com a animação de transição personalizada. Quando definido, modalPresentationStyle = .custom viewWillAppearnão chamado

na classe de animação de transição personalizada precisam de métodos de chamada: beginAppearanceTransitioneendAppearanceTransition


0

No meu caso, esse foi apenas um bug estranho no emulador ios 12.1. Desapareceu após o lançamento no dispositivo real.


0

Criei uma classe que resolve esse problema. Basta configurá-lo como um delegado de seu controlador de navegação e implementar um ou dois métodos simples em seu controlador de visualização - que será chamado quando a visualização estiver prestes a ser mostrada ou tiver sido mostrada via NavigationController

Aqui está o GIST mostrando o código


0

ViewWillAppear é um método de substituição da classe UIViewController, portanto, adicionar um subView não chamará viewWillAppear, mas quando você apresentar, empurrar, pop, mostrar, setFront ou popToRootViewController de um viewController então viewWillAppear para viewController apresentado será chamado.


0

Meu problema era que viewWillAppear não era chamado ao sair de uma segue. A resposta foi fazer uma chamada para viewWillAppear (true) na segue de desenrolamento no Controlador de Visualização para a qual você segue

@IBAction func desenrolou (para desenrolamento: UIStoryboardSegue, ViewController subseqüenteVC: Qualquer) {

   viewWillAppear(true)
}

-2

Não tenho certeza se esse é o mesmo problema que resolvi.
Em algumas ocasiões, o método não é executado da maneira normal, como "[self methodOne]".

Experimentar

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}

o problema é que viewWillAppearnão é chamado
Vyachaslav Gerchicov

-3

Você deve ter apenas 1 UIViewController ativo a qualquer momento. Quaisquer subvisualizações que você deseja manipular devem ser exatamente isso - subVIEWS - ou seja, UIView.

Eu uso uma técnica simples para gerenciar minha hierarquia de visualizações e ainda não tive nenhum problema desde que comecei a fazer as coisas dessa maneira. Existem 2 pontos principais:

  • um único UIViewController deve ser usado para gerenciar "o valor de uma tela" do seu aplicativo
  • use UINavigationController para alterar as visualizações

O que quero dizer com "vale uma tela"? É um pouco vago propositalmente, mas geralmente é um recurso ou seção do seu aplicativo. Se você tem algumas telas com a mesma imagem de fundo, mas diferentes sobreposições / popups etc., isso deve ser um controlador de visualização e várias visualizações filho. Você nunca deve trabalhar com 2 controladores de visualização. Observe que você ainda pode instanciar um UIView em um controlador de exibição e adicioná-lo como uma subvisão de outro controlador de exibição se quiser que certas áreas da tela sejam mostradas em vários controladores de exibição.

Quanto ao UINavigationController - este é o seu melhor amigo! Desligue a barra de navegação e especifique NÃO para animado, e você terá uma excelente maneira de alternar as telas sob demanda. Você pode empurrar e pop controladores de visualização se eles estiverem em uma hierarquia, ou você pode preparar uma matriz de controladores de visualização (incluindo uma matriz contendo um único VC) e configurá-la para ser a pilha de visualização usando setViewControllers. Isso lhe dá total liberdade para trocar os VCs, enquanto obtém todas as vantagens de trabalhar dentro do modelo esperado da Apple e fazer com que todos os eventos etc. sejam acionados corretamente.

Aqui está o que eu faço sempre que inicio um aplicativo:

  • comece a partir de um aplicativo baseado em janela
  • adicione um UINavigationController como o rootViewController da janela
  • adicione o que eu quiser que meu primeiro UIViewController seja o rootViewController do controlador de navegação

(observe que começar com base na janela é apenas uma preferência pessoal - eu gosto de construir as coisas sozinho, então sei exatamente como elas são construídas. Deve funcionar bem com o modelo baseado em visualização)

Todos os eventos disparam corretamente e basicamente a vida é boa. Você pode então gastar todo o seu tempo escrevendo as partes importantes do seu aplicativo e não se preocupando em tentar hackear manualmente as hierarquias de visualização.


Não há nada de errado em ter vários controladores de visualização ativos; UIViewController possui métodos para permitir que uma hierarquia seja construída (por exemplo, [addChildViewController:]).
Richard de

1
Sim, agora. Em 2011, isso não aconteceu. A resposta estava correta na época, reconhecidamente não é agora.
Nigel Flack
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.