web-dev-qa-db-fra.com

iPhone viewWillAppear ne tire pas

J'ai lu de nombreux articles sur des personnes ayant des problèmes avec viewWillAppear lorsque vous ne créez pas votre hiérarchie de vues juste right. Mon problème est que je ne peux pas comprendre ce que cela signifie.

Si je crée une RootViewController et appelle addSubView sur ce contrôleur, les vues ajoutées seront câblées pour les événements viewWillAppear

Quelqu'un a-t-il un exemple de hiérarchie complexe de vues programmatiques recevant avec succès des événements viewWillAppear à tous les niveaux?

État de la documentation d'Apple:

Avertissement: Si la vue appartenant à un contrôleur de vue est ajoutée directement à une hiérarchie de vues, ce dernier ne recevra pas ce message. Si vous insérez ou ajoutez une vue à la hiérarchie de vues et que celle-ci comporte un contrôleur de vue, vous devez envoyer ce message directement au contrôleur de vue associé. À défaut d'envoyer ce message au contrôleur de la vue, toute animation associée ne sera pas affichée.

Le problème est qu'ils ne décrivent pas comment faire cela. Que signifie "directement"? Comment ajoutez-vous "indirectement" une vue?

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

111
chzk

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égation du contrôleur de navigation à la place:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
52
mmalc

J'ai rencontré ce même problème. Envoyez simplement un message viewWillAppear à votre contrôleur de vue avant de l'ajouter en tant que sous-vue. (Un paramètre BOOL indique au contrôleur de vue s'il est animé ou non.)

[myViewController viewWillAppear:NO];

Regardez RootViewController.m dans l'exemple Metronome. 

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

29
lajos

J'ai finalement trouvé une solution pour ce 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 d’implémenter UINavigationControllerDelegate et ses deux méthodes. Brillant! Je suis tellement excitée d'avoir enfin trouvé une solution!

18
Chris

Je viens d'avoir le même problème. Dans mon application, j'ai 2 contrôleurs de navigation et le même contrôleur de vue dans chacun d'eux a fonctionné dans un cas et non dans l'autre. Je veux dire que lorsque vous appuyez exactement sur le même contrôleur de vue dans la première UINavigationController, viewWillAppear a été appelé, mais pas lorsque vous avez poussé le second contrôleur de navigation.

Puis je suis tombé sur ce post UINavigationController doit appeler les méthodes viewWillAppear/viewWillDisappear

Et réalisé que mon deuxième contrôleur de navigation a 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 substituez cette méthode, vous devez appeler super à un moment donné de votre implémentation.

7
Antoine

J'ai utilisé un contrôleur de navigation. Lorsque je souhaite descendre à un autre niveau de données ou afficher ma vue personnalisée, j'utilise les éléments suivants:

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

Lorsque je le fais, la fonction viewWillAppear est activée. Je suppose que cela est considéré comme "indirect" parce que je n’appelle pas moi-même la méthode addSubView. 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 cela vous donnera peut-être un indice.

5
Josh Gagnon

Tout d'abord, la barre d'onglets doit être située 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/UINavigationBarDelegate pour transférer les notifications manuellement, mais j’ai constaté que pour que toute la hiérarchie des appels de vue fonctionne correctement, il me suffisait 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). À partir de là, il a correctement appelé ces méthodes sur ses contrôleurs de vue enfant.

Ma hiérarchie est comme ça:

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 de tabulation/navigation pour la première fois garantissait que TOUS les événements étaient transférés correctement. Cela m'a évité de devoir les appeler manuellement à partir des méthodes UINavigationBarDelegate/UITabBarControllerDelegate.

Sidenote: Curieusement, quand ça ne marchait pas, la méthode privée 

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

.. que vous pouvez voir depuis le callstack sur une implémentation qui fonctionne, appelle généralement les méthodes viewWill/Did.. mais ne l’a pas fait jusqu’à ce que j’ai effectué ce qui précède (même si elle a été appelée).

Je pense qu'il est TRÈS important que la UITabBarController soit au niveau de la fenêtre et que les documents semblent le confirmer.

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

4
Sam

Les vues sont ajoutées "directement" en appelant [view addSubview:subview]. Les vues sont ajoutées "indirectement" à l'aide de méthodes telles que les barres de tabulation 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 bien OUI selon votre cas).

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

3
AndrewS

Pour ce faire, utilisez 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];
}
3
Hari Kunwar

Comme aucune réponse n'est acceptée et que des 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 les méthodes viewWillAppear/Dissappear etc.

- (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 sous cette vue, j'ai une étiquette une image et le conteneur (une autre vue) où je mets le contrôleur. J'ai dû brouiller certaines choses car c'était un travail pour un client.

alt text

3
hol

Merci iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppear et ViewDidAppear ne seront pas appelés sur un contrôleur de présentation sur 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.

enter image description here

2
BilalReffas

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 ça marche bien pour moi.

2
Arash Zeinoddini

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

2
giuseppe
[self.navigationController setDelegate:self];

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

1
Gaurav

Pour Swift. Commencez par créer le protocole pour appeler ce que vous voulez appeler dans viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Deuxièmement, créer 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 ayant accès au contrôleur de navigation et existe tant que le contrôleur de navigation existe. Ce peut être par exemple le contrôleur de vue racine du contrôleur de navigation ou la classe qui le crée ou le présente. Affectez ensuite l'instance de ForceUpdateOnViewAppear à la propriété de délégué du contrôleur de navigation le plus tôt possible. 

1
Vadim Motorine

Je pense que l'ajout d'une sous-vue ne signifie pas nécessairement que la vue va apparaître. Il n'y a donc pas d'appel automatique à la méthode de la classe.

1
winston

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

L'utilisation de ce modèle est le seul moyen d'obtenir les méthodes Will/Did/Apparaître/Désapparaître appelées sur l'objet ViewControllers lors du Push/Pop de ces contrôleurs dans UINavigationController. Aucune des autres solutions dans les réponses ici ne fonctionnait pour moi, y compris leur implémentation dans RootController et leur transmission à (l'enfant) NavigationController. Ces fonctions (volonté/apparition/disparition) n’ont été appelées dans mon RootController qu’après avoir affiché/masqué les VCs de niveau supérieur, mes "identifiants" et navigateursCV, et non les sous-VCs du contrôleur de navigation. "les passer" au Nav VC.

J'ai fini par utiliser la fonctionnalité de délégué de 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 pour obtenir à la fois la fonctionnalité de disparition et d'affichage "simulée".

Aussi, c'est une question de principe de le faire fonctionner après m'être cogné la tête contre ce problème pendant des heures. Tous les extraits de code fonctionnels utilisant un RootController personnalisé et une navigation enfant VC seraient grandement appréciés.

1
Bogatyr

Au cas où cela aiderait quelqu'un. J'ai eu un problème similaire où ma ViewWillAppear ne tire pas sur une UITableViewController. Après avoir beaucoup joué, je me suis rendu compte que le problème était que la UINavigationController qui contrôle ma UITableView ne se trouvait pas dans la vue racine. Une fois que j'ai résolu ce problème, cela fonctionne maintenant comme un champion.

1
Andrew

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

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

J'espère que cela pourra aider

1
Finn Gaida

Je ne suis pas tout à fait sûr à ce sujet, mais je pense que l'ajout d'une vue à la hiérarchie consiste à appeler -addSubview: dans la vue du contrôleur de vue (par exemple, [viewController.view addSubview:anotherViewController.view]) au lieu de placer un nouveau contrôleur de vue dans la pile du contrôleur de navigation.

1
Martin Gordon

ViewWillAppear est une méthode de substitution de la classe UIViewController. Par conséquent, l'ajout d'une sous-vue n'appelle pas viewWillAppear, mais vous devez appeler Push, pop, show, setFront ou popToRootViewController.

0
Abu Ul Hassan

J'ai créé une classe qui résout ce problème. Définissez-le simplement en tant que 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

0
GregJaskiewicz

Dans mon cas, il s’agissait d’un étrange bug sur l’émulateur iOS 12.1. Disparu après le lancement sur un périphérique réel.

0
Burning

Dans mon cas, le problème était lié à l’animation de transition personnalisée . Lorsque modalPresentationStyle = .customviewWillAppear était défini

dans la classe d'animation de transition personnalisée, il faut utiliser les méthodes suivantes: beginAppearanceTransition et endAppearanceTransition

0
ober