iPhone viewWillAppear non si attiva


116

Ho letto molti post su persone che hanno problemi con viewWillAppearquando non si crea la gerarchia vista solo a destra. Il mio problema è che non riesco a capire cosa significhi.

Se creo un RootViewControllere chiamo addSubViewquel controller, mi aspetto che le viste aggiunte siano cablate per gli viewWillAppeareventi.

Qualcuno ha un esempio di una complessa gerarchia di viste programmatiche che riceve con successo gli viewWillAppeareventi a ogni livello?

Stato di Documenti di Apple:

Avviso: se la vista appartenente a un controller di visualizzazione viene aggiunta direttamente a una gerarchia di visualizzazione, il controller di visualizzazione non riceverà questo messaggio. Se si inserisce o si aggiunge una vista alla gerarchia della vista e questa ha un controller della vista, è necessario inviare questo messaggio direttamente al controller della vista associato. L'impossibilità di inviare il controller di visualizzazione questo messaggio impedirà la visualizzazione di qualsiasi animazione associata.

Il problema è che non descrivono come farlo. Cosa significa "direttamente"? Come si aggiunge "indirettamente" una vista?

Sono abbastanza nuovo per Cocoa e iPhone, quindi sarebbe bello se ci fossero esempi utili di Apple oltre alla schifezza di Hello World di base.


Ho avuto questo problema fino a quando non mi sono reso conto che stavo capendo male l'uso previsto delle sottoclassi di UIViewController in generale. Dai un'occhiata a questa domanda. stackoverflow.com/questions/5691226/...
averydev

7
Attenzione !!! Non è più vero su iOS 5 !!! Chiamate viewWillAppear e viewDidAppear automaticamente
Vilém Kurz

Risposte:


55

Se si utilizza un controller di navigazione e si imposta il suo delegato, i metodi di visualizzazione {Will, Did} {Appear, Disappear} non vengono richiamati.

È necessario utilizzare invece i metodi delegati del controller di navigazione:

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

2
Non avevo impostato il delegato del controller di navigazione e il metodo non veniva ancora chiamato. Ad ogni modo, l'ho impostato e poi ho usato i metodi che hai menzionato sopra. Grazie.
Dimitris

Vedo la stessa cosa di Dimitris
jkp

7
L'ho appena testato in iOS4 e iOS5: NON è vero: l'impostazione del delegato di un controller di navigazione e quindi il push di una vista su di esso si
attiverà

Swift 3: func navigationController (_ navigationController: UINavigationController, willshow viewController: UIViewController, animato: Bool) {} E func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animato: Bool) {}
Naloiko Eugene

28

Mi sono imbattuto in questo stesso problema. Basta inviare un viewWillAppearmessaggio al controller della vista prima di aggiungerlo come sottoview. (C'è un parametro BOOL che dice al controller della vista se è animato in modo che appaia o meno.)

[myViewController viewWillAppear:NO];

Guarda RootViewController.m nell'esempio Metronome.

(In realtà ho trovato fantastici i progetti di esempio di Apple. C'è molto di più di HelloWorld;)


3
In realtà, dovresti chiamare viewWillAppear dopo averlo aggiunto alla sottoview. In caso contrario, IBOutlet / IBActions non verranno cablati.
4th Space

2
Sì, dopo. Vista secondaria creata da XIB, viewWillAppear non è stata chiamata. Chiamalo da solo e tutto funziona alla perfezione.
JOM

Grazie! Questo è stato esattamente per me. Stavo aggiungendo manualmente una sottoview tramite [scrollView addSubview:controller.view];. Ho aggiunto la riga in [controller viewWillAppear:NO];seguito e voilà! Ha funzionato come un fascino.
Rob S.

Con ogni probabilità, ciò è dovuto al fatto che il tuo UIViewController sta controllando una vista che è una vista secondaria di una vista controllata da un altro UIViewController. Questo non è il modello di progettazione previsto. Per ulteriori spiegazioni dai un'occhiata a questo post. stackoverflow.com/questions/5691226/...
averydev

6
Attenzione !!! Non è più vero su iOS 5 !!! Chiama automaticamente viewWillAppear e viewDidAppear. Se lo chiamassi manualmente, verrebbe chiamato due volte.
Vilém Kurz

18

Finalmente ho trovato una soluzione per questo CHE FUNZIONA!

UINavigationControllerDelegate

Penso che il succo sia impostare il delegato del controllo di navigazione sul viewcontroller in cui si trova e implementare UINavigationControllerDelegatei due metodi. Brillante! Sono così eccitato che ho finalmente trovato una soluzione!


come assegnare un rootviewcontroller come delegato per navigationcontroller?
Gargo

1
NON FUNZIONA! prova a ridurre a icona l'app e ingrandirla
Vyachaslav Gerchicov

8

Ho appena avuto lo stesso problema. Nella mia applicazione ho 2 controller di navigazione e premendo lo stesso controller di visualizzazione in ciascuno di essi ha funzionato in un caso e non nell'altro. Voglio dire che quando si spingeva lo stesso identico controller di visualizzazione nel primo UINavigationController, viewWillAppearveniva chiamato ma non quando veniva premuto nel secondo controller di navigazione.

Poi mi sono imbattuto in questo post UINavigationController dovrebbe chiamare i metodi viewWillAppear / viewWillDisappear

E ho capito che il mio secondo controller di navigazione è stato ridefinito viewWillAppear. Lo screening del codice ha mostrato che non stavo chiamando

[super viewWillAppear:animated];

L'ho aggiunto e ha funzionato!

La documentazione dice:

Se esegui l'override di questo metodo, devi chiamare super a un certo punto della tua implementazione.


Stessa cosa qui. Incasinato non chiamando super.
olivaresF

5

Sto usando un controller di navigazione. Quando voglio scendere a un altro livello di dati o mostrare la mia visualizzazione personalizzata, utilizzo quanto segue:

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

Quando lo faccio, ottengo la viewWillAppearfunzione da attivare. Suppongo che questo si qualifichi come "indiretto" perché non sto chiamando il addSubViewmetodo effettivo da solo. Non so se questo è applicabile al 100% alla tua applicazione poiché non posso dire se stai utilizzando un controller di navigazione, ma forse fornirà un indizio.


5

Grazie iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearE ViewDidAppearnon andranno chiamata su una presentazione di controller della vista su iOS 13 che utilizza una nuova presentazione modale che non copre l'intero schermo.

I crediti vanno ad Arek Holko . Mi ha davvero salvato la giornata.

inserisci qui la descrizione dell'immagine


4

In primo luogo, la barra delle schede dovrebbe essere a livello di root, cioè aggiunta alla finestra, come indicato nella documentazione Apple. Questa è la chiave per un comportamento corretto.

In secondo luogo, puoi utilizzare UITabBarDelegate/ UINavigationBarDelegateper inoltrare manualmente le notifiche, ma ho scoperto che per far funzionare correttamente l'intera gerarchia delle chiamate di visualizzazione, tutto ciò che dovevo fare era chiamare manualmente

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

e

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

.. solo UNA VOLTA prima di impostare i controller di visualizzazione sul rispettivo controller (subito dopo l'assegnazione). Da quel momento in poi, ha chiamato correttamente questi metodi sui suoi controller di visualizzazione figlio.

La mia gerarchia è così:

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

La prima chiamata ai metodi menzionati sul controller tab / nav ha assicurato che TUTTI gli eventi fossero inoltrati correttamente. Mi ha impedito di chiamarli manualmente dai metodi UINavigationBarDelegate/ UITabBarControllerDelegate.

Nota a margine: Curiosamente, quando non funzionava, il metodo privato

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

.. che puoi vedere dallo stack di chiamate su un'implementazione funzionante, di solito chiama i viewWill/Did..metodi ma non lo faceva fino a quando non ho eseguito quanto sopra (anche se è stato chiamato).

Penso che sia MOLTO importante che UITabBarControllersia a livello di finestra e i documenti sembrano confermarlo.

Spero che sia stato chiaro (ish), felice di rispondere ad ulteriori domande.


3

Poiché nessuna risposta è accettata e le persone (come me) atterrano qui, do la mia variazione. Anche se non sono sicuro che fosse il problema originale. Quando il controller di navigazione viene aggiunto come sottoview a un'altra vista, devi chiamare tu stesso i metodi viewWillAppear / Dissappear ecc. In questo modo:

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

    [subNavCntlr viewWillAppear:animated];
}

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

    [subNavCntlr viewWillDisappear:animated];
}

Giusto per completare l'esempio. Questo codice viene visualizzato nel mio ViewController dove ho creato e aggiunto il controller di navigazione in una vista che ho posizionato sulla vista.

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

il .h ha questo aspetto

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

@end

Nel file pennino ho la vista e sotto questa vista ho un'etichetta un'immagine e il contenitore (un'altra vista) dove metto il controller. Ecco come appare. Ho dovuto rimescolare alcune cose perché questo era il lavoro per un cliente.

testo alternativo


3

Le visualizzazioni vengono aggiunte "direttamente" chiamando [view addSubview:subview]. Le visualizzazioni vengono aggiunte "indirettamente" tramite metodi quali le barre delle schede o le barre di navigazione che scambiano le visualizzazioni secondarie.

Ogni volta che chiami [view addSubview:subviewController.view], dovresti chiamare [subviewController viewWillAppear:NO](o SÌ a seconda del tuo caso).

Ho riscontrato questo problema quando ho implementato il mio sistema di gestione della visualizzazione root personalizzato per una schermata secondaria in un gioco. L'aggiunta manuale della chiamata a viewWillAppear ha risolto il mio problema.


3

Il modo corretto per farlo è usare l'API di contenimento 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];
}

Questa è assolutamente la soluzione moderna corretta (iOS 9,8,7). Inoltre, se stai cambiando al volo il controller di visualizzazione incorporato, dovrai chiamare [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke

1
In questo caso viewWillAppear:potrebbe ancora non essere chiamato
Vyachaslav Gerchicov

2

Uso questo codice per i controller push e pop view:

spingere:

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

pop:

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

.. e funziona bene per me.


2

Un errore molto comune è il seguente. Lei ha un punto di vista, UIView* ae un altro, UIView* b. Aggiungi b ad a come sottoview. Se provi a chiamare viewWillAppear in b, non verrà mai attivato, perché è una sottoview di a


2

iOS 13 ha messo la mia app nel sedere qui. Se hai notato un cambiamento di comportamento a partire da iOS 13, imposta quanto segue prima di spingerlo:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Potrebbe anche essere necessario impostarlo nel tuo .storyboard nell'inspector Attributi (imposta Presentazione su Schermo intero).

In questo modo la tua app si comporterà come nelle versioni precedenti di iOS.


1

Non ne sono sicuro al 100%, ma penso che aggiungere una vista direttamente alla gerarchia della vista significhi chiamare -addSubview:la vista del controller della vista (ad esempio [viewController.view addSubview:anotherViewController.view]) invece di spingere un nuovo controller della vista sullo stack del controller di navigazione.


1

Penso che l'aggiunta di una vista secondaria non significhi necessariamente che la vista apparirà, quindi non c'è una chiamata automatica al metodo della classe che verrà


1

Penso che ciò che intendono "direttamente" sia collegare le cose allo stesso modo del modello xcode "Navigation Application", che imposta UINavigationController come unica sottoview della UIWindow dell'applicazione.

L'utilizzo di quel modello è l'unico modo in cui sono stato in grado di ottenere i metodi Will / Did / Appear / Disappear chiamati sull'oggetto ViewControllers su push / pop di quei controller in UINavigationController. Nessuna delle altre soluzioni nelle risposte qui ha funzionato per me, inclusa l'implementazione nel RootController e il passaggio al (figlio) NavigationController. Quelle funzioni (appariranno / appariranno / scompariranno) sono state chiamate nel mio RootController solo dopo aver mostrato / nascosto i VC di primo livello, i miei "login" e iVC di navigazione, non i sub-VC nel controller di navigazione, quindi non ho avuto la possibilità di farlo "passarli attraverso" al Nav VC.

Ho finito per utilizzare la funzionalità delegato di UINavigationController per cercare le transizioni particolari che richiedevano funzionalità di follow-up nella mia app, e che funziona, ma richiede un po 'più di lavoro per ottenere sia la funzionalità di scomparsa che di visualizzazione "simulata".

Inoltre è una questione di principio farlo funzionare dopo aver sbattuto la testa contro questo problema per ore oggi. Qualsiasi frammento di codice funzionante che utilizzi un RootController personalizzato e un VC di navigazione figlio sarebbe molto apprezzato.


1

Nel caso questo aiuti qualcuno. Ho avuto un problema simile in cui il mio ViewWillAppearnon sparava su un UITableViewController. Dopo aver giocato molto, mi sono reso conto che il problema era che il UINavigationControllercontrollo di my UITableViewnon si trovava nella visualizzazione root. Una volta risolto il problema, ora funziona come un campione.


Puoi per favore condividere "come" hai fatto?
Brabbeldas

1

Ho avuto questo problema da solo e mi ci sono volute 3 ore intere (di cui 2 su Google) per risolverlo.

Ciò che si è rivelato utile è stato semplicemente eliminare l'app dal dispositivo / simulatore, pulirla e quindi eseguirla di nuovo .

spero che aiuti


1
[self.navigationController setDelegate:self];

Imposta il delegato sul controller di visualizzazione radice.


1

Per Swift. Per prima cosa crea il protocollo per chiamare ciò che vuoi chiamare in viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Secondo, crea la classe

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

}

Terzo, fai in modo che l'istanza di ForceUpdateOnViewAppear sia il membro della classe appropriata che ha accesso al controller di navigazione ed esista fintanto che esiste il controller di navigazione. Può essere, ad esempio, il controller di visualizzazione principale del controller di navigazione o la classe che lo crea o lo presenta. Quindi assegna l'istanza di ForceUpdateOnViewAppear alla proprietà delegato del controller di navigazione il prima possibile.


0

Nel mio caso il problema era con l'animazione di transizione personalizzata. Quando impostato modalPresentationStyle = .custom viewWillAppearnon viene chiamato

nella classe di animazione di transizione personalizzata sono necessari metodi di chiamata: beginAppearanceTransitioneendAppearanceTransition


0

Nel mio caso quello era solo uno strano bug sull'emulatore iOS 12.1. Scomparso dopo l'avvio sul dispositivo reale.


0

Ho creato una classe che risolve questo problema. Basta impostarlo come delegato del controller di navigazione e implementare uno o due metodi semplici nel controller della visualizzazione, che verrà chiamato quando la visualizzazione sta per essere mostrata o è stata mostrata tramite NavigationController

Ecco il GIST che mostra il codice


0

ViewWillAppear è un metodo di override della classe UIViewController, quindi l'aggiunta di un subView non chiamerà viewWillAppear, ma quando presenterai, push, pop, show, setFront o popToRootViewController da un viewController, verrà chiamato viewWillAppear per viewController presentato.


0

Il mio problema era che viewWillAppear non veniva chiamato quando si tornava da un segue. La risposta è stata quella di mettere una chiamata a viewWillAppear (true) nel segue di svolgimento nel controller di visualizzazione a cui stai seguendo

@IBAction func unfind (per unfindSegue: UIStoryboardSegue, ViewController nextVC: Any) {

   viewWillAppear(true)
}

-2

Non sono sicuro che questo sia lo stesso problema che ho risolto.
In alcune occasioni, il metodo non viene eseguito con modalità normali come "[self methodOne]".

Provare

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

il problema è che viewWillAppearnon è affatto chiamato
Vyachaslav Gerchicov

-3

Dovresti avere solo 1 UIViewController attivo in qualsiasi momento. Tutte le sottoview che vuoi manipolare dovrebbero essere esattamente quelle - subVIEWS - cioè UIView.

Uso una tecnica semplice per gestire la mia gerarchia di visualizzazione e devo ancora incontrare un problema da quando ho iniziato a fare le cose in questo modo. Ci sono 2 punti chiave:

  • un singolo UIViewController dovrebbe essere utilizzato per gestire "il valore di uno schermo" della tua app
  • utilizzare UINavigationController per modificare le visualizzazioni

Cosa intendo per "valore di uno schermo"? È un po 'vago di proposito, ma generalmente è una funzionalità o una sezione della tua app. Se hai alcune schermate con la stessa immagine di sfondo ma diverse sovrapposizioni / popup ecc., Dovrebbe essere 1 controller di visualizzazione e diverse visualizzazioni figlio. Non dovresti mai ritrovarti a lavorare con 2 controller di visualizzazione. Si noti che è comunque possibile creare un'istanza di una vista UIV in un controller di visualizzazione e aggiungerla come sottoview di un altro controller di visualizzazione se si desidera che determinate aree dello schermo vengano mostrate in più controller di visualizzazione.

Per quanto riguarda UINavigationController, questo è il tuo migliore amico! Disattiva la barra di navigazione e specifica NO per animato, e avrai un modo eccellente per cambiare schermata su richiesta. È possibile eseguire il push e il pop dei controller di visualizzazione se si trovano in una gerarchia oppure è possibile preparare un array di controller di visualizzazione (incluso un array contenente un singolo VC) e impostarlo come stack di visualizzazione utilizzando setViewControllers. Questo ti dà la totale libertà di cambiare VC, ottenendo tutti i vantaggi di lavorare all'interno del modello previsto di Apple e di ottenere tutti gli eventi ecc.

Ecco cosa faccio ogni volta che avvio un'app:

  • inizia da un'app basata su finestre
  • aggiungere un UINavigationController come rootViewController della finestra
  • aggiungi quello che voglio che sia il mio primo UIViewController come rootViewController del controller di navigazione

(nota che iniziare da una finestra è solo una preferenza personale: mi piace costruire le cose da solo, quindi so esattamente come sono costruite. Dovrebbe funzionare bene con il modello basato sulla visualizzazione)

Tutti gli eventi si attivano correttamente e fondamentalmente la vita è buona. Puoi quindi dedicare tutto il tuo tempo a scrivere i pezzi importanti della tua app e non scherzare cercando di modificare manualmente le gerarchie di visualizzazione in forma.


Non c'è niente di sbagliato nell'avere più controller di visualizzazione attivi; UIViewController ha metodi per consentire la costruzione di una gerarchia (ad esempio [addChildViewController:]).
Richard

1
Sì, adesso. Nel 2011 non è stato così. La risposta era accurata all'epoca, devo ammettere che non lo è ora.
Nigel Flack,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.