iPhone viewWillAppear wird nicht ausgelöst


116

Ich habe zahlreiche Beiträge über Personen gelesen, mit viewWillAppeardenen Probleme auftreten, wenn Sie Ihre Ansichtshierarchie nicht genau richtig erstellen . Mein Problem ist, dass ich nicht herausfinden kann, was das bedeutet.

Wenn ich einen Controller erstelle RootViewControllerund addSubViewdiesen aufrufe, würde ich erwarten, dass die hinzugefügten Ansichten für viewWillAppearEreignisse verkabelt werden .

Hat jemand ein Beispiel für eine komplexe programmatische Ansichtshierarchie, die viewWillAppearEreignisse auf allen Ebenen erfolgreich empfängt ?

Apples Docs-Status:

Warnung: Wenn die zu einem Ansichtscontroller gehörende Ansicht direkt zu einer Ansichtshierarchie hinzugefügt wird, erhält der Ansichtscontroller diese Nachricht nicht. Wenn Sie der Ansichtshierarchie eine Ansicht einfügen oder hinzufügen und diese über einen Ansichtscontroller verfügt, sollten Sie dem zugeordneten Ansichtscontroller diese Nachricht direkt senden. Wenn der View Controller nicht gesendet wird, wird verhindert, dass zugehörige Animationen angezeigt werden.

Das Problem ist, dass sie nicht beschreiben, wie das geht. Was bedeutet "direkt"? Wie fügt man "indirekt" eine Ansicht hinzu?

Ich bin ziemlich neu in Cocoa und iPhone, daher wäre es schön, wenn es neben dem einfachen Hello World-Mist nützliche Beispiele von Apple gäbe.


Ich hatte dieses Problem, bis mir klar wurde, dass ich die beabsichtigte Verwendung von UIViewController-Unterklassen im Allgemeinen falsch verstand. Schauen Sie sich diese Frage an. stackoverflow.com/questions/5691226/…
averydev

7
Bitte aufgepasst !!! Gilt nicht mehr für iOS 5 !!! Ruft viewWillAppear und viewDidAppear automatisch auf
Vilém Kurz

Antworten:


55

Wenn Sie einen Navigationscontroller verwenden und seinen Delegaten festlegen, werden die Methoden view {Will, Did} {Appear, Disappear} nicht aufgerufen.

Sie müssen stattdessen die Delegate-Methoden des Navigationscontrollers verwenden:

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

2
Ich hatte den Delegaten meines Navigationscontrollers nicht festgelegt und trotzdem wurde die Methode nicht aufgerufen. Wie auch immer, ich habe es eingestellt und dann die oben genannten Methoden verwendet. Vielen Dank.
Dimitris

Ich sehe das gleiche wie Dimitris
jkp

7
Ich habe dies gerade in iOS4 und iOS5 getestet: Dies ist NICHT wahr: Wenn Sie den Delegaten eines Navigationscontrollers festlegen und dann eine Ansicht darauf verschieben, wird viewWillAppear: usw.
ausgelöst

Swift 3: func Navigation (_ Navigation: UINavigationController, willshow Viewcontroller: UIViewController, animierte: bool) {} UND func Navigation (_ Navigation: UINavigationController, didShow Viewcontroller: UIViewController, animierte: bool) {}
Naloiko Eugene

28

Ich bin auf dasselbe Problem gestoßen. Senden Sie einfach eine viewWillAppearNachricht an Ihren View Controller, bevor Sie sie als Unteransicht hinzufügen. (Es gibt einen BOOL-Parameter, der dem Ansichts-Controller mitteilt, ob die Animation angezeigt wird oder nicht.)

[myViewController viewWillAppear:NO];

Schauen Sie sich RootViewController.m im Metronom-Beispiel an.

(Ich fand Apples Beispielprojekte wirklich großartig. Es gibt viel mehr als HelloWorld;)


3
Eigentlich sollten Sie viewWillAppear aufrufen, nachdem Sie es der Unteransicht hinzugefügt haben. Andernfalls werden IBOutlets / IBActions nicht verkabelt.
4thSpace

2
Ja, danach. ViewWillAppear wurde aus XIB erstellt und nicht aufgerufen. Nennen Sie es selbst und alles funktioniert gut.
JOM

Danke dir! Das war genau das für mich. Ich habe manuell eine Unteransicht über hinzugefügt [scrollView addSubview:controller.view];. Ich habe die Zeile [controller viewWillAppear:NO];danach hinzugefügt und voila! Lief wie am Schnürchen.
Rob S.

Dies liegt höchstwahrscheinlich daran, dass Ihr UIViewController eine Ansicht steuert, die eine Unteransicht einer Ansicht ist, die von einem anderen UIViewController gesteuert wird. Dies ist nicht das beabsichtigte Entwurfsmuster. Weitere Erklärungen finden Sie in diesem Beitrag. stackoverflow.com/questions/5691226/…
averydev

6
Bitte aufgepasst !!! Gilt nicht mehr für iOS 5 !!! Ruft viewWillAppear und viewDidAppear automatisch auf. Wenn Sie es manuell aufrufen, wird es zweimal aufgerufen.
Vilém Kurz

18

Ich habe endlich eine Lösung dafür gefunden, DAS FUNKTIONIERT!

UINavigationControllerDelegate

Ich denke, das Wesentliche ist, den Delegaten Ihres Navigationssteuerelements auf den Viewcontroller zu setzen, in dem es sich befindet, und es zu implementieren, UINavigationControllerDelegateund es sind zwei Methoden. Brillant! Ich bin so aufgeregt, dass ich endlich eine Lösung gefunden habe!


Wie ordne ich einen Rootviewcontroller als Delegaten für den Navigationscontroller zu?
Gargo

1
Es funktioniert nicht! Versuchen Sie, die App zu minimieren und zu maximieren
Vyachaslav Gerchicov

8

Ich hatte gerade das gleiche Problem. In meiner Anwendung habe ich 2 Navigations-Controller und das Drücken des gleichen View-Controllers in jedem von ihnen hat in einem Fall funktioniert und nicht in dem anderen. Ich meine , dass , wenn genau die gleichen Ansicht - Controller in dem ersten Druck UINavigationController, viewWillAppeargenannt wurde , aber nicht , wenn sie in der zweiten Navigationssteuerung gedrückt.

Dann bin ich auf diesen Beitrag gestoßen. UINavigationController sollte die Methoden viewWillAppear / viewWillDisappear aufrufen

Und erkannte, dass mein zweiter Navigationscontroller neu definiert wurde viewWillAppear. Das Überprüfen des Codes zeigte, dass ich nicht anrief

[super viewWillAppear:animated];

Ich habe es hinzugefügt und es hat funktioniert!

Die Dokumentation sagt:

Wenn Sie diese Methode überschreiben, müssen Sie irgendwann in Ihrer Implementierung super aufrufen.


Das selbe hier. Durcheinander, nicht super anzurufen.
olivaresF

5

Ich habe einen Navigationscontroller verwendet. Wenn ich entweder auf eine andere Datenebene absteigen oder meine benutzerdefinierte Ansicht anzeigen möchte, verwende ich Folgendes:

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

Wenn ich das mache, bekomme ich die viewWillAppearFunktion zu feuern. Ich nehme an, dies gilt als "indirekt", da ich die eigentliche addSubViewMethode nicht selbst aufrufe. Ich weiß nicht, ob dies zu 100% auf Ihre Anwendung zutrifft, da ich nicht sagen kann, ob Sie einen Navigationscontroller verwenden, aber möglicherweise gibt es einen Hinweis.


5

Danke iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearUnd ViewDidAppearnicht erhält auf einer Präsentation View - Controller auf iOS 13 , die eine neue modale Präsentation verwendet , die nicht den ganzen Bildschirm abdeckt genannt.

Credits gehen an Arek Holko . Er hat mir wirklich den Tag gerettet.

Geben Sie hier die Bildbeschreibung ein


4

Erstens sollte sich die Registerkartenleiste auf der Stammebene befinden, dh dem Fenster hinzugefügt werden, wie in der Apple-Dokumentation angegeben. Dies ist der Schlüssel für ein korrektes Verhalten.

Zweitens Sie können verwendet werden UITabBarDelegate/ UINavigationBarDelegatedie Meldungen manuell weiterleiten, aber ich fand , dass die ganze Hierarchie der Ansicht Anrufe an der Arbeit richtig zu bekommen, alles , was ich hatte , war manuell Aufruf zu tun

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

und

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

.. nur EINMAL vor dem Einrichten der View-Controller auf dem jeweiligen Controller (direkt nach der Zuordnung). Von da an hat es diese Methoden auf seinen untergeordneten Ansichtscontrollern korrekt aufgerufen.

Meine Hierarchie ist wie folgt:

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

Durch das erstmalige Aufrufen der genannten Methoden auf dem Tab / Nav-Controller wurde sichergestellt, dass ALLE Ereignisse korrekt weitergeleitet wurden. Ich musste sie nicht mehr manuell über die UINavigationBarDelegate/ UITabBarControllerDelegate-Methoden aufrufen .

Nebenbemerkung: Seltsamerweise, wenn es nicht funktioniert hat, die private Methode

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

.. was Sie aus dem Callstack einer funktionierenden Implementierung ersehen können, ruft normalerweise die viewWill/Did..Methoden auf, hat dies aber erst getan, als ich das oben genannte ausgeführt habe (obwohl es aufgerufen wurde).

Ich denke, es ist SEHR wichtig, dass sich das UITabBarControllerauf Fensterebene befindet, und die Dokumente scheinen dies zu belegen.

Hoffe das war klar (ish), gerne weitere Fragen beantworten.


3

Da keine Antwort akzeptiert wird und Leute (wie ich) hier landen, gebe ich meine Variation. Obwohl ich nicht sicher bin, ob das das ursprüngliche Problem war. Wenn der Navigationscontroller als Unteransicht zu einer anderen Ansicht hinzugefügt wird, müssen Sie die Methoden viewWillAppear / Dissappear usw. wie folgt aufrufen:

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

    [subNavCntlr viewWillAppear:animated];
}

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

    [subNavCntlr viewWillDisappear:animated];
}

Nur um das Beispiel zu vervollständigen. Dieser Code wird in meinem ViewController angezeigt, in dem ich den Navigationscontroller erstellt und zu einer Ansicht hinzugefügt habe, die ich in der Ansicht platziert habe.

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

das .h sieht so aus

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

@end

In der NIB-Datei habe ich die Ansicht und unter dieser Ansicht habe ich eine Beschriftung, ein Bild und den Container (eine andere Ansicht), in den ich den Controller eingefügt habe. So sieht es aus. Ich musste einige Dinge durcheinander bringen, da dies Arbeit für einen Kunden war.

Alt-Text


3

Ansichten werden "direkt" durch Aufrufen hinzugefügt [view addSubview:subview]. Ansichten werden "indirekt" durch Methoden wie Registerkartenleisten oder Navigationsleisten hinzugefügt, die Unteransichten austauschen.

Jedes Mal [view addSubview:subviewController.view], wenn Sie anrufen , sollten Sie anrufen [subviewController viewWillAppear:NO](oder JA, je nach Fall).

Ich hatte dieses Problem, als ich mein eigenes benutzerdefiniertes Root-View-Verwaltungssystem für einen Subscreen in einem Spiel implementierte. Durch manuelles Hinzufügen des Aufrufs zu viewWillAppear wurde mein Problem behoben.


3

Der richtige Weg, dies zu tun, ist die Verwendung der UIViewController-Containment-API.

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

Dies ist absolut die richtige moderne Lösung (iOS 9,8,7). Wenn Sie den eingebetteten Ansichts-Controller im laufenden Betrieb ändern, müssen Sie außerdem [viewController willMoveToParentViewController: nil] aufrufen. [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke

1
In diesem Fall viewWillAppear:kann immer noch nicht genannt werden
Vyachaslav Gerchicov

2

Ich verwende diesen Code für Push- und Pop-View-Controller:

drücken:

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

Pop:

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

.. und es funktioniert gut für mich.


2

Ein sehr häufiger Fehler ist wie folgt. Sie haben eine Ansicht UIView* aund eine andere UIView* b. Sie fügen b zu a als Unteransicht hinzu. Wenn Sie versuchen, viewWillAppear in b aufzurufen, wird es niemals ausgelöst, da es sich um eine Unteransicht von a handelt


2

iOS 13 hat meine App hier in den Hintern gebissen. Wenn Sie eine Verhaltensänderung ab iOS 13 bemerkt haben, stellen Sie einfach Folgendes ein, bevor Sie darauf klicken:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Möglicherweise müssen Sie es auch in Ihrem .storyboard im Attributinspektor festlegen (Präsentation auf Vollbild setzen).

Dadurch verhält sich Ihre App wie in früheren Versionen von iOS.


1

Ich bin mir nicht 100% sicher, aber ich denke, dass das Hinzufügen einer Ansicht zur Ansichtshierarchie direkt das Aufrufen -addSubview:der Ansicht des Ansichtscontrollers (z. B. [viewController.view addSubview:anotherViewController.view]) bedeutet, anstatt einen neuen Ansichtscontroller auf den Stapel des Navigationscontrollers zu verschieben.


1

Ich denke, dass das Hinzufügen einer Unteransicht nicht unbedingt bedeutet, dass die Ansicht angezeigt wird, sodass die Methode der Klasse nicht automatisch aufgerufen wird


1

Ich denke, was sie "direkt" bedeuten, ist, die Dinge genauso anzuschließen wie die xcode-Vorlage "Navigationsanwendung", die den UINavigationController als einzige Unteransicht des UIWindow der Anwendung festlegt.

Die Verwendung dieser Vorlage ist die einzige Möglichkeit, die für das Objekt ViewControllers aufgerufenen Will / Did / Appear / Disappear-Methoden beim Push / Pops dieser Controller im UINavigationController abzurufen. Keine der anderen Lösungen in den Antworten hier hat für mich funktioniert, einschließlich der Implementierung im RootController und der Weitergabe an den (untergeordneten) NavigationController. Diese Funktionen (werden / haben / erscheinen / verschwinden) wurden in meinem RootController nur aufgerufen, wenn die VCs der obersten Ebene, meine "Login" - und Navigations-VCs angezeigt / ausgeblendet wurden, nicht die Sub-VCs im Navigations-Controller, sodass ich keine Gelegenheit dazu hatte "Weiterleiten" an die Nav VC.

Am Ende habe ich die Delegate-Funktionalität des UINavigationController verwendet, um nach bestimmten Übergängen zu suchen, für die in meiner App Follow-up-Funktionen erforderlich sind. Dies funktioniert, erfordert jedoch etwas mehr Arbeit, damit sowohl die Funktionen zum Verschwinden als auch zum Anzeigen "simuliert" werden.

Es ist auch eine Grundsatzfrage, es zum Laufen zu bringen, nachdem ich heute stundenlang gegen dieses Problem gekämpft habe. Alle Arbeitscode-Schnipsel, die einen benutzerdefinierten RootController und eine untergeordnete Navigations-VC verwenden, sind sehr willkommen.


1

Falls dies jemandem hilft. Ich hatte ein ähnliches Problem, bei dem ich ViewWillAppearnicht auf einen feuere UITableViewController. Nachdem ich viel herumgespielt hatte, stellte ich fest, dass das Problem darin bestand, dass das UINavigationController, das meine steuert, UITableViewnicht in der Stammansicht ist. Sobald ich das behoben habe, funktioniert es jetzt wie ein Champion.


Können Sie uns bitte mitteilen, wie Sie das gemacht haben?
Brabbeldas

1

Ich hatte dieses Problem gerade selbst und es dauerte 3 volle Stunden (von denen 2 googelten), um es zu beheben.

Was sich als hilfreich herausstellte, war, die App einfach vom Gerät / Simulator zu löschen, zu bereinigen und dann erneut auszuführen .

hoffentlich hilft das


1
[self.navigationController setDelegate:self];

Stellen Sie den Delegaten auf den Root-View-Controller ein.


1

Für Swift. Erstellen Sie zunächst das Protokoll, um das aufzurufen, was Sie in viewWillAppear aufrufen möchten

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Zweitens erstellen Sie die Klasse

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

}}

Drittens müssen Sie die Instanz von ForceUpdateOnViewAppear als Mitglied der entsprechenden Klasse festlegen, die Zugriff auf den Navigationscontroller hat und so lange vorhanden ist, wie der Navigationscontroller vorhanden ist. Dies kann beispielsweise der Root-View-Controller des Navigationscontrollers oder die Klasse sein, die ihn erstellt oder präsentiert. Weisen Sie dann die Instanz von ForceUpdateOnViewAppear so früh wie möglich der Delegate-Eigenschaft von Navigation Controller zu.


0

In meinem Fall war das Problem mit der benutzerdefinierten Übergangsanimation. Wenn eingestellt modalPresentationStyle = .custom viewWillAppearnicht aufgerufen

in benutzerdefinierten Übergangsanimationsklassen benötigen Aufrufmethoden: beginAppearanceTransitionundendAppearanceTransition


0

In meinem Fall war das nur ein seltsamer Fehler im ios 12.1-Emulator. Verschwindet nach dem Start auf einem realen Gerät.


0

Ich habe eine Klasse erstellt, die dieses Problem löst. Legen Sie es einfach als Delegat Ihres Navigationscontrollers fest und implementieren Sie ein oder zwei einfache Methoden in Ihrem Ansichtscontroller. Diese werden aufgerufen, wenn die Ansicht angezeigt werden soll oder über NavigationController angezeigt wurde

Hier ist die GIST, die den Code zeigt


0

ViewWillAppear ist eine Überschreibungsmethode der UIViewController-Klasse. Wenn Sie also eine Unteransicht hinzufügen, wird viewWillAppear nicht aufgerufen. Wenn Sie jedoch einen viewController präsentieren, drücken, popen, anzeigen, setFront oder popToRootViewController anzeigen, wird viewWillAppear für den präsentierten viewController aufgerufen.


0

Mein Problem war, dass viewWillAppear beim Abwickeln aus einem Segue nicht aufgerufen wurde. Die Antwort bestand darin, viewWillAppear (true) im Abwicklungsbereich des View Controllers aufzurufen, zu dem Sie zurückkehren

@IBAction func unwind (für unwindSegue: UIStoryboardSegue, ViewController anschließendVC: Beliebig) {

   viewWillAppear(true)
}

-2

Ich bin mir nicht sicher, ob dies das gleiche Problem ist, das ich gelöst habe.
In einigen Fällen wird die Methode nicht auf normale Weise ausgeführt, z. B. "[self methodOne]".

Versuchen

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

Das Problem wird überhaupt viewWillAppearnicht genannt
Vyachaslav Gerchicov

-3

Sie sollten immer nur 1 UIViewController aktiv haben. Alle Unteransichten, die Sie bearbeiten möchten, sollten genau das sein - Unteransichten - dh UIView.

Ich verwende eine einfache Technik zum Verwalten meiner Ansichtshierarchie und bin noch nicht auf ein Problem gestoßen, seit ich angefangen habe, Dinge auf diese Weise zu tun. Es gibt 2 wichtige Punkte:

  • Ein einzelner UIViewController sollte verwendet werden, um den "Wert eines Bildschirms" Ihrer App zu verwalten
  • Verwenden Sie UINavigationController zum Ändern von Ansichten

Was meine ich mit "ein Bildschirm ist wert"? Es ist absichtlich etwas vage, aber im Allgemeinen ist es eine Funktion oder ein Abschnitt Ihrer App. Wenn Sie einige Bildschirme mit demselben Hintergrundbild, aber unterschiedlichen Überlagerungen / Popups usw. haben, sollte dies 1 Ansichts-Controller und mehrere untergeordnete Ansichten sein. Sie sollten niemals mit 2 View Controllern arbeiten. Beachten Sie, dass Sie eine UIView weiterhin in einem Ansichts-Controller instanziieren und als Unteransicht eines anderen Ansichts-Controllers hinzufügen können, wenn bestimmte Bereiche des Bildschirms in mehreren Ansichts-Controllern angezeigt werden sollen.

UINavigationController - das ist dein bester Freund! Schalten Sie die Navigationsleiste aus und geben Sie NEIN für animiert an. Sie haben eine hervorragende Möglichkeit, den Bildschirm bei Bedarf zu wechseln. Sie können Ansichts-Controller verschieben und einfügen, wenn sie sich in einer Hierarchie befinden, oder Sie können ein Array von Ansichts-Controllern (einschließlich eines Arrays mit einer einzelnen VC) vorbereiten und mithilfe von setViewControllern als Ansichtsstapel festlegen. Dies gibt Ihnen die völlige Freiheit, VCs zu ändern und gleichzeitig alle Vorteile der Arbeit mit dem von Apple erwarteten Modell zu nutzen und alle Ereignisse usw. ordnungsgemäß auszulösen.

Folgendes mache ich jedes Mal, wenn ich eine App starte:

  • Starten Sie von einer fensterbasierten App
  • Fügen Sie einen UINavigationController als rootViewController des Fensters hinzu
  • Fügen Sie hinzu, was mein erster UIViewController als rootViewController des Navigationscontrollers sein soll

(Hinweis, ausgehend von fensterbasiert ist nur eine persönliche Präferenz - ich konstruiere Dinge gerne selbst, damit ich genau weiß, wie sie erstellt werden. Es sollte gut mit ansichtsbasierten Vorlagen funktionieren.)

Alle Ereignisse werden korrekt ausgelöst und im Grunde ist das Leben gut. Sie können dann Ihre ganze Zeit damit verbringen, die wichtigen Teile Ihrer App zu schreiben und nicht zu versuchen, Ansichtshierarchien manuell in Form zu hacken.


Es ist nichts Falsches daran, mehrere View Controller aktiv zu haben. UIViewController verfügt über Methoden, mit denen eine Hierarchie erstellt werden kann (z. B. [addChildViewController:]).
Richard

1
Ja, das tut es jetzt. Im Jahr 2011 war dies nicht der Fall. Die Antwort war zu der Zeit richtig, zugegebenermaßen nicht jetzt.
Nigel Flack
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.