La vue de l'iPhone n'apparaîtra pas

116

J'ai lu de nombreux articles sur des personnes ayant des problèmes viewWillAppearlorsque vous ne créez pas simplement votre hiérarchie de vues à droite. Mon problème est que je n'arrive pas à comprendre ce que cela signifie.

Si je crée un RootViewControlleret appelle addSubViewsur ce contrôleur, je m'attendrais à ce que la ou les vues ajoutées soient câblées pour les viewWillAppearévénements.

Quelqu'un a-t-il un exemple de hiérarchie de vues programmatique complexe qui reçoit avec succès les viewWillAppearévénements à tous les niveaux?

État des documents d'Apple:

Avertissement: Si la vue appartenant à un contrôleur de vue est ajoutée directement à une hiérarchie de vues, le contrôleur de vue ne recevra pas ce message. Si vous insérez ou ajoutez une vue à la hiérarchie de vues et qu'elle possède un contrôleur de vue, vous devez envoyer ce message directement au contrôleur de vue associé. Si vous ne transmettez pas ce message au contrôleur de vue, toute animation associée ne sera pas affichée.

Le problème est qu'ils ne décrivent pas comment procéder. Que signifie «directement»? Comment ajouter "indirectement" une vue?

Je suis assez nouveau sur Cocoa et iPhone, donc ce serait bien s'il y avait des exemples utiles d'Apple en plus de la merde de base Hello World.

chzk
la source
J'ai eu ce problème jusqu'à ce que je réalise que je comprenais mal l'utilisation prévue des sous-classes UIViewController en général. Consultez cette question. stackoverflow.com/questions/5691226/…
averydev
7
Attention !!! Ce n'est plus vrai sur iOS 5 !!! Appels viewWillAppear et viewDidAppear automatiquement
Vilém Kurz

Réponses:

55

Si vous utilisez un contrôleur de navigation et définissez son délégué, les méthodes view {Will, Did} {Appear, Disappear} ne sont pas appelées.

Vous devez utiliser les méthodes de délégué du contrôleur de navigation à la place:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
mmalc
la source
2
Je n'avais pas défini le délégué de mon contrôleur de navigation et la méthode n'était toujours pas appelée. Quoi qu'il en soit, je l'ai défini et ensuite j'ai utilisé les méthodes que vous mentionnez ci-dessus. Merci.
Dimitris
Je vois la même chose que Dimitris
jkp
7
Je viens de tester cela dans iOS4 et iOS5: Ce n'est PAS vrai: définir le délégué d'un navigationController, puis pousser une vue sur celui-ci, déclenchera viewWillAppear: etc.
DaGaMs
Swift 3: func NavigationController (_ NavigationController: UINavigationController, willShow viewController: UIViewController, animé: Bool) {} ET func NavigationController (_ NavigationController: UINavigationController, didShow viewController: UIViewController, animé: Bool) {}
Naloiko Eugene
28

J'ai rencontré ce même problème. Envoyez simplement un viewWillAppearmessage à votre contrôleur de vue avant de l'ajouter en tant que sous-vue. (Il existe un paramètre BOOL qui indique au contrôleur de vue s'il est en cours d'animation pour apparaître ou non.)

[myViewController viewWillAppear:NO];

Regardez RootViewController.m dans l'exemple Metronome.

(En fait, j'ai trouvé les projets d'exemple d'Apple excellents. Il y a BEAUCOUP plus que HelloWorld;)

lajos
la source
3
En fait, vous devez appeler viewWillAppear après l'avoir ajouté à la sous-vue. Sinon, les IBOutlets / IBActions ne seront pas câblés.
4thSpace
2
Oui, après. Créé sous-vue à partir de XIB, viewWillAppear n'a pas été appelé. Appelez-le par moi-même et tout fonctionne très bien.
JOM
Je vous remercie! C'était exactement ça pour moi. J'ajoutais manuellement une sous-vue via [scrollView addSubview:controller.view];. J'ai ajouté la ligne [controller viewWillAppear:NO];après et voilà! A fonctionné comme un charme.
Rob S.
Selon toute vraisemblance, c'est parce que votre UIViewController contrôle une vue qui est une sous-vue d'une vue contrôlée par un autre UIViewController. Ce n'est pas le modèle de conception prévu. Pour plus d'explications, consultez cet article. stackoverflow.com/questions/5691226/…
averydev
6
Attention !!! Ce n'est plus vrai sur iOS 5 !!! Appelle viewWillAppear et viewDidAppear automatiquement. Si vous l'appeliez manuellement, il serait appelé deux fois.
Vilém Kurz
18

J'ai enfin trouvé une solution pour cela qui fonctionne!

UINavigationControllerDelegate

Je pense que l'essentiel est de définir le délégué de votre contrôle de navigation sur le contrôleur de vue dans lequel il se trouve, et de mettre en œuvre UINavigationControllerDelegateet ce sont deux méthodes. Brillant! Je suis tellement excité que j'ai enfin trouvé une solution!

Chris
la source
comment affecter un rootviewcontroller en tant que délégué pour navigationcontroller?
Gargo
1
Ça ne marche pas! essayez de minimiser l'application et de la maximiser
Vyachaslav Gerchicov
8

J'ai juste eu le même problème. Dans mon application, j'ai 2 contrôleurs de navigation et pousser le même contrôleur de vue dans chacun d'eux fonctionnait dans un cas et pas dans l'autre. Je veux dire que lorsque vous appuyez sur le même contrôleur de vue dans le premier UINavigationController,viewWillAppear été appelé mais pas lorsqu'il est enfoncé dans le deuxième contrôleur de navigation.

Ensuite, je suis tombé sur ce post UINavigationController devrait appeler les méthodes viewWillAppear / viewWillDisappear

Et j'ai réalisé que mon deuxième contrôleur de navigation avait été redéfini viewWillAppear. Le filtrage du code a montré que je n'appelais pas

[super viewWillAppear:animated];

Je l'ai ajouté et cela a fonctionné!

La documentation dit:

Si vous remplacez cette méthode, vous devez appeler super à un moment donné de votre implémentation.

Antoine
la source
Même chose ici. Foiré en n'appelant pas super.
olivaresF
5

J'utilise un contrôleur de navigation. Lorsque je souhaite descendre à un autre niveau de données ou afficher ma vue personnalisée, j'utilise ce qui suit:

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

Lorsque je fais cela, je viewWillAppeardéclenche la fonction. Je suppose que cela se qualifie comme «indirect» parce que je n'appelle pas addSubViewmoi-même la méthode réelle . Je ne sais pas si cela s'applique à 100% à votre application car je ne peux pas dire si vous utilisez un contrôleur de navigation, mais peut-être que cela fournira un indice.

Josh Gagnon
la source
5

Merci iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppearEt ViewDidAppearne se fait appel à un contrôleur de vue sur la présentation iOS 13 qui utilise une nouvelle présentation modale qui ne couvre pas tout l'écran.

Les crédits vont à Arek Holko . Il a vraiment sauvé ma journée.

entrez la description de l'image ici

BilalReffas
la source
4

Tout d'abord, la barre d'onglets doit être au niveau racine, c'est-à-dire ajoutée à la fenêtre, comme indiqué dans la documentation Apple. C'est la clé d'un comportement correct.

Deuxièmement, vous pouvez utiliser UITabBarDelegate/ UINavigationBarDelegatepour transférer les notifications manuellement, mais j'ai trouvé que pour que toute la hiérarchie des appels de vue fonctionne correctement, tout ce que j'avais à faire était d'appeler manuellement

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

et

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

.. juste UNE FOIS avant de configurer les contrôleurs de vue sur le contrôleur respectif (juste après l'attribution). Dès lors, il a correctement appelé ces méthodes sur ses contrôleurs de vue enfants.

Ma hiérarchie est comme ceci:

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

Le simple fait d'appeler les méthodes mentionnées sur le contrôleur tab / nav la première fois garantissait que TOUS les événements étaient correctement transmis. Cela m'a empêché de devoir les appeler manuellement à partir des méthodes UINavigationBarDelegate/ UITabBarControllerDelegate.

Sidenote: Curieusement, quand cela ne fonctionnait pas, la méthode privée

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

.. que vous pouvez voir à partir de la pile d'appels sur une implémentation de travail, appelle généralement les viewWill/Did..méthodes mais ne l'a pas fait jusqu'à ce que j'ai effectué ce qui précède (même si cela a été appelé).

Je pense qu'il est TRÈS important que le UITabBarControllersoit au niveau de la fenêtre et les documents semblent étayer cela.

J'espère que c'était clair (ish), heureux de répondre à d'autres questions.

Sam
la source
3

Comme aucune réponse n'est acceptée et que les gens (comme moi) atterrissent ici, je donne ma variante. Bien que je ne sois pas sûr que ce soit le problème initial. Lorsque le contrôleur de navigation est ajouté en tant que sous-vue à une autre vue, vous devez appeler vous-même les méthodes viewWillAppear / Dissappear etc. comme ceci:

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

    [subNavCntlr viewWillAppear:animated];
}

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

    [subNavCntlr viewWillDisappear:animated];
}

Juste pour rendre l'exemple complet. Ce code apparaît dans mon ViewController où j'ai créé et ajouté le contrôleur de navigation dans une vue que j'ai placée sur la vue.

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

le .h ressemble à ceci

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

@end

Dans le fichier nib, j'ai la vue et en dessous de cette vue, j'ai une étiquette une image et le conteneur (une autre vue) où je mets le contrôleur. Voici à quoi il ressemble. J'ai dû brouiller certaines choses car c'était du travail pour un client.

texte alternatif

hol
la source
3

Les vues sont ajoutées "directement" en appelant [view addSubview:subview]. Les vues sont ajoutées «indirectement» par des méthodes telles que les barres d'onglets ou les barres de navigation qui permutent les sous-vues.

Chaque fois que vous appelez [view addSubview:subviewController.view], vous devez alors appeler [subviewController viewWillAppear:NO](ou OUI selon votre cas).

J'ai eu ce problème lorsque j'ai implémenté mon propre système de gestion personnalisé de la vue racine pour un sous-écran dans un jeu. L'ajout manuel de l'appel à viewWillAppear a résolu mon problème.

AndrewS
la source
3

La bonne façon de faire est d'utiliser l'api de confinement 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];
}
Hari Kunwar
la source
C'est absolument la solution moderne correcte (iOS 9,8,7). De plus, si vous modifiez le contrôleur de vue intégré à la volée, vous devrez appeler [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
Eli Burke
1
Dans ce cas, viewWillAppear:peut toujours ne pas être appelé
Vyachaslav Gerchicov
2

J'utilise ce code pour les contrôleurs de vue push et pop:

pousser:

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

pop:

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

.. et cela fonctionne très bien pour moi.

Arash Zeinoddini
la source
2

Une erreur très courante est la suivante. Vous avez un point de vue, UIView* aet un autre, UIView* b. Vous ajoutez b à a en tant que sous-vue. Si vous essayez d'appeler viewWillAppear en b, il ne sera jamais déclenché, car il s'agit d'une sous-vue d'un

Giuseppe
la source
2

iOS 13 a mordu mon application dans le cul ici. Si vous avez remarqué un changement de comportement à partir d'iOS 13, définissez simplement ce qui suit avant de le pousser:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Vous devrez peut-être également le définir dans votre .storyboard dans l'inspecteur d'attributs (définissez Présentation sur Plein écran).

Cela permettra à votre application de se comporter comme dans les versions précédentes d'iOS.

poupée
la source
1

Je ne suis pas sûr à 100% à ce sujet, mais je pense que l'ajout d'une vue à la hiérarchie des vues signifie directement appeler -addSubview:la vue du contrôleur de vue (par exemple,[viewController.view addSubview:anotherViewController.view] ) au lieu de pousser un nouveau contrôleur de vue sur la pile du contrôleur de navigation.

Martin Gordon
la source
1

Je pense que l'ajout d'une sous-vue ne signifie pas nécessairement que la vue apparaîtra, il n'y a donc pas d'appel automatique à la méthode de la classe pour qu'elle


la source
1

Je pense que ce qu'ils veulent dire "directement" est de raccorder les choses de la même manière que le modèle xcode "Navigation Application", qui définit UINavigationController comme la seule sous-vue de l'UIWindow de l'application.

L'utilisation de ce modèle est la seule façon dont j'ai pu obtenir les méthodes Will / Did / Apparear / Disappear appelées sur l'objet ViewControllers lors de la poussée / pop de ces contrôleurs dans UINavigationController. Aucune des autres solutions dans les réponses ici n'a fonctionné pour moi, y compris leur implémentation dans RootController et leur transmission au (enfant) NavigationController. Ces fonctions (vont / ont / apparaissent / disparaissent) n'ont été appelées dans mon RootController qu'après avoir affiché / masqué les VC de niveau supérieur, mes «login» et navigationVC, pas les sous-VC dans le contrôleur de navigation, donc je n'ai pas eu l'occasion de "les transmettre" au Nav VC.

J'ai fini par utiliser la fonctionnalité de délégué d'UINavigationController pour rechercher les transitions particulières qui nécessitaient une fonctionnalité de suivi dans mon application, et cela fonctionne, mais cela nécessite un peu plus de travail afin d'obtenir à la fois la fonctionnalité disparaître et apparaître "simulée".

C'est aussi une question de principe de le faire fonctionner après m'être cogné la tête contre ce problème pendant des heures aujourd'hui. Tout extrait de code fonctionnel utilisant un RootController personnalisé et un VC de navigation enfant serait très apprécié.

Bogatyr
la source
1

Au cas où cela aiderait quelqu'un. J'ai eu un problème similaire où mon ViewWillAppearne tire pas sur un UITableViewController. Après avoir beaucoup joué, j'ai réalisé que le problème était que le UINavigationControllercontrôle de mon UITableViewn'est pas sur la vue racine. Une fois que j'ai corrigé cela, cela fonctionne maintenant comme un champion.

Andrew
la source
Pouvez-vous s'il vous plaît partager "comment" vous avez fait cela?
Brabbeldas
1

Je viens d'avoir ce problème moi-même et il m'a fallu 3 heures complètes (dont 2 sur Google) pour le résoudre.

Ce qui s'est avéré utile était simplement de supprimer l'application de l'appareil / simulateur, de la nettoyer puis de la relancer .

J'espère que cela pourra aider

Finn Gaida
la source
1
[self.navigationController setDelegate:self];

Définissez le délégué sur le contrôleur de vue racine.

Gaurav
la source
1

Pour Swift. Créez d'abord le protocole pour appeler ce que vous vouliez appeler dans viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Deuxièmement, créez la classe

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

}

Troisièmement, faites de l'instance de ForceUpdateOnViewAppear le membre de la classe appropriée qui a accès au contrôleur de navigation et existe tant que le contrôleur de navigation existe. Il peut s'agir par exemple du contrôleur de vue racine du contrôleur de navigation ou de la classe qui le crée ou le présente. Attribuez ensuite l'instance de ForceUpdateOnViewAppear à la propriété de délégué du contrôleur de navigation dès que possible.

Vadim Motorine
la source
0

Dans mon cas, le problème était avec une animation de transition personnalisée. Lorsqu'il modalPresentationStyle = .custom viewWillAppearn'est pas appelé

dans la classe d'animation de transition personnalisée ont besoin de méthodes d'appel: beginAppearanceTransitionetendAppearanceTransition

ober
la source
0

Dans mon cas, ce n'était qu'un bug étrange sur l'émulateur ios 12.1. Disparu après le lancement sur un appareil réel.

froid
la source
0

J'ai créé une classe qui résout ce problème. Définissez-le simplement comme délégué de votre contrôleur de navigation et implémentez une ou deux méthodes simples dans votre contrôleur de vue - qui seront appelées lorsque la vue est sur le point d'être affichée ou a été affichée via NavigationController

Voici le GIST montrant le code

GregJaskiewicz
la source
0

ViewWillAppear est une méthode de substitution de la classe UIViewController, donc l'ajout d'un sousView n'appellera pas viewWillAppear, mais lorsque vous présentez, push, pop, show, setFront Ou popToRootViewController à partir d'un viewController, alors viewWillAppear pour le viewController présenté sera appelé.

Abu Ul Hassan
la source
0

Mon problème était que viewWillAppear n'était pas appelé lors du déroulement d'un segue. La réponse était de mettre un appel à viewWillAppear (true) dans la séquence de déroulement dans le contrôleur de vue auquel vous revenez

@IBAction func unind (pour unindSegue: UIStoryboardSegue, ViewController nextVC: Any) {

   viewWillAppear(true)
}
user2385491
la source
-2

Je ne suis pas sûr que ce soit le même problème que j'ai résolu.
Dans certaines occasions, la méthode ne s'exécute pas de manière normale comme "[self methodOne]".

Essayer

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
Sean
la source
le problème n'est viewWillAppearpas du tout appelé
Vyachaslav Gerchicov
-3

Vous ne devriez avoir qu'un seul UIViewController actif à tout moment. Toutes les sous-vues que vous souhaitez manipuler doivent être exactement cela - subVIEWS - c'est-à-dire UIView.

J'utilise une technique simple pour gérer ma hiérarchie de vues et je n'ai pas encore rencontré de problème depuis que j'ai commencé à faire les choses de cette façon. Il y a 2 points clés:

  • un seul UIViewController doit être utilisé pour gérer "la valeur d'un écran" de votre application
  • utiliser UINavigationController pour modifier les vues

Qu'est-ce que j'entends par «la valeur d'un écran»? C'est un peu vague exprès, mais il s'agit généralement d'une fonctionnalité ou d'une section de votre application. Si vous avez quelques écrans avec la même image d'arrière-plan mais des superpositions / fenêtres contextuelles différentes, etc., cela devrait être 1 contrôleur de vue et plusieurs vues enfants. Vous ne devriez jamais travailler avec 2 contrôleurs de vue. Notez que vous pouvez toujours instancier un UIView dans un contrôleur de vue et l'ajouter en tant que sous-vue d'un autre contrôleur de vue si vous souhaitez que certaines zones de l'écran soient affichées dans plusieurs contrôleurs de vue.

Quant à UINavigationController - c'est votre meilleur ami! Désactivez la barre de navigation et spécifiez NON pour les animations, et vous disposez d'un excellent moyen de changer d'écran à la demande. Vous pouvez pousser et afficher les contrôleurs de vue s'ils se trouvent dans une hiérarchie, ou vous pouvez préparer un tableau de contrôleurs de vue (y compris un tableau contenant un seul VC) et le définir comme la pile de vues à l'aide de setViewControllers. Cela vous donne une liberté totale pour changer les VC, tout en bénéficiant de tous les avantages de travailler dans le modèle attendu d'Apple et de déclencher correctement tous les événements, etc.

Voici ce que je fais à chaque fois que je lance une application:

  • démarrer à partir d'une application basée sur une fenêtre
  • ajouter un UINavigationController en tant que rootViewController de la fenêtre
  • ajouter ce que je veux que mon premier UIViewController soit en tant que rootViewController du contrôleur de navigation

(notez que le départ basé sur la fenêtre est juste une préférence personnelle - j'aime construire des choses moi-même pour savoir exactement comment elles sont construites. Cela devrait fonctionner correctement avec un modèle basé sur la vue)

Tous les événements se déclenchent correctement et fondamentalement, la vie est belle. Vous pouvez ensuite passer tout votre temps à écrire les éléments importants de votre application sans vous soucier d'essayer de pirater manuellement les hiérarchies de vue.

Nigel Flack
la source
Il n'y a rien de mal à avoir plusieurs contrôleurs de vue actifs; UIViewController a des méthodes pour permettre à une hiérarchie d'être construite (par exemple [addChildViewController:]).
Richard
1
Oui c'est le cas maintenant. En 2011, non. La réponse était exacte à l'époque, certes, ce n'est pas le cas maintenant.
Nigel Flack