Avertissement: -La présentation de contrôleurs de vue sur des contrôleurs de vue détachés est déconseillée


180

Dans mon application, j'utilise un contrôleur de navigation. Plus tard, dans une vue que j'utilise presentViewControllerpour afficher une image agrandie. De plus, je n'utilise pas de Storyboard ou de plume.

Je reçois cette erreur dans iOS 7 uniquement. Cela fonctionne bien dans iOS 6 et les versions antérieures:

La présentation de contrôleurs de vue sur des contrôleurs de vue détachés est déconseillée


Je n'ai pas encore compris. Mais dans mon application, je n'attribue aucun viewcontroller à window.rootviewcontroller. j'ajoute une vue à la fenêtre. C'est peut-être la raison pour moi. mais pas sûr ...
Gagan Joshi

@GaganJoshi La raison que vous avez mentionnée ci-dessus n'est peut-être pas la cause. Même moi, je suis confronté au même problème. Et dans notre projet, j'attribue un contrôleur de vue à window.rootviewcontroller.
Rajesh

1
Je pense que les autres commentaires relient correctement cela à quelque chose sur le rootViewController et la connexion de la fenêtre. Je n'ai pas tout à fait compris cela, mais j'ai pu contourner le problème en présentant le contrôleur directement sur le rootViewController plutôt que sur le contrôleur de navigation ou l'un de ses enfants.
Rich Waters

Réponses:


210

Pour éviter de recevoir l'avertissement dans une navigation push, vous pouvez utiliser directement:

[self.view.window.rootViewController presentViewController:viewController animated:YES completion:nil];

Et puis dans votre contrôleur de vue modale, lorsque tout est terminé, vous pouvez simplement appeler:

[self dismissViewControllerAnimated:YES completion:nil];


Je présente le sélecteur d'images avec ce code de ligne "[self.view.window.rootViewController presentViewController: viewController animé: OUI achèvement: nil];" Mais impossible de rejeter la vue de poicker avec cette ligne "[self ignoreViewControllerAnimated: YES completion: nil];" Toute autre option pour le licenciement du contrôleur
kb920

@keyurbhalodiya Vous devez appeler la méthode licenciementViewController à partir du modalView pour la faire fonctionner. Ainsi, si vous affichez une vue nommée viewB depuis une viewA avec [viewA.window.rootViewController presentViewController: viewB], dans viewB vous devez ajouter un bouton par exemple, associé à une action personnalisée appelant [self ignoreViewControllerAnimated]. Est-ce plus clair?
cdescours

11
Ne présente pas viewcontroller dans iOS 8.
Rajesh Maurya

1
pour iOS 8: [self.view.window.rootViewController.navigationController
Fede Cugliandolo

31
l'utilisation l'a self.navigationControllerfait pour moi.
Brandon Zacharie

65

Attendre viewDidAppear() :

Cette erreur peut également survenir si vous essayez de présenter le contrôleur de vue avant que la vue n'apparaisse réellement, par exemple en présentant une vue dans viewWillAppear()ou avant. Essayez de présenter une autre vue après viewDidAppear()ou à l'intérieur.


10
En d'autres termes, ne présentez aucun contrôleur de vue dans viewDidLoad(), people! J'ai fait cette erreur tant de fois ...
T Blank

Merci cela a aidé. J'avais du code dans viewDidLoad où il a essayé d'afficher une boîte de dialogue à la fin.
ArdenDev

J'obtiens cette erreur lors de l'exécution de tests unitaires / d'intégration où je ne teste pas avec des animations.
mixtly87

Ouais, c'était exactement comme tu l'as dit! Si ce n'est pas ce message, je devrais probablement refactoriser.
kikiwora

62

La raison de cet avertissement est que je présentais un contrôleur de vue sur une petite vue qui n'est pas une vue en taille réelle. Ci-dessous, l'image de mon projet. où cliquez sur quatre options ci-dessus. L'utilisateur accède à une vue différente de childviewcontroller (cela fonctionne comme tabViewcontroller). Mais le childviewcontroller contient une vue de petite taille. Donc, si nous présentons une vue de childviewcontroller, cela donne cet avertissement.

vue de détail principale

Et pour éviter cela, vous pouvez présenter une vue sur le parent de childviewcontroller

  [self.parentViewController presentViewController:viewController animated:YES completion:nil];

1
[self.view.window.rootViewController.navigationController pushViewController: YOUR_VIEW_CONTROLER animé: OUI];
Fede Cugliandolo

1
"présentait un contrôleur de vue sur une petite vue qui n'est pas une vue en taille réelle." ... EXACTEMENT. Bon travail.
Fattie

22

Dans mon cas, j'ai sampleViewControllerajouté une vue de sous en tant que sous-vue, puis essaie de présenter un popover à partir de la vue de sampleViewController(ici à la selfplace une UIViewControllerinstance):

[self.view addSubview:sampleViewController.view];

La bonne manière devrait être ci-dessous:

// make sure the vc has been added as a child view controller as well
[self addChildViewController:sampleViewController];
[self.view addSubview:sampleViewController.view];
[sampleViewController didMoveToParentViewController:self];

Btw, cela fonctionne également pour le cas qui présente un popover d'une cellule tableview, il vous suffit de vous assurer que le contrôleur tableview a également été ajouté en tant que contrôleur de vue enfant.


Appelez en outre didMoveToParentViewController. Veuillez consulter Ajouter et supprimer ChildViewController: gist.github.com/tomohisa/2897676
Jakehao

@jianzong je me souviens qu'il n'est pas nécessaire de faire la dernière étape. Quoi qu'il en soit, permettez-moi de l'ajouter, merci pour la suggestion. :)
Kjuly

Oui, cela fonctionnera sans la dernière étape. Je pense que le but est d'informer le parentViewController afin qu'il appelle certaines méthodes pour faire quelque chose. :)
Jakehao

2
cela fonctionne pour moi, j'utilise la vue des contrôleurs dans un autre contrôleur - (Vue du conteneur par programme), je n'ai pas ajouté [self addChildViewController:sampleViewController];, maintenant j'ai ajouté ceci, merci
anjnkmr

16

Je pense que le problème est que vous n'avez pas une hiérarchie de contrôleur de vue appropriée. Définissez le rootviewcontroller de l'application, puis affichez de nouvelles vues en poussant ou en présentant de nouveaux contrôleurs de vue dessus. Laissez chaque contrôleur de vue gérer ses vues. Seuls les contrôleurs de vue de conteneur, comme le tabbarviewcontroller, devraient ajouter d'autres vues de contrôleurs de vue à leurs propres vues. Lisez le guide de programmation des contrôleurs de vue pour en savoir plus sur l'utilisation correcte des contrôleurs de vue. https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/


15

Swift 3

Pour quiconque trébuche là-dessus, voici la réponse rapide.

self.parent?.present(viewController, animated: true, completion: nil)

9

J'ai presque le même problème. La raison était que j'ai essayé de présenter "un" contrôleur sur un autre et une fois l'animation terminée, je définissais le contrôleur présenté en tant que root. Après cette opération, tous les autres contrôleurs qui se présentent m'amènent à l'avertissement: «La présentation de contrôleurs de vue sur des contrôleurs de vue détachés est déconseillée ". Et je résous cet avertissement juste en paramétrant "un" contrôleur en tant que root sans aucune présentation au début.

Supprimé:

[[self rootController] presentViewController:controller animated:YES completion:^{

       [self window].rootViewController = controller;

       [[self window] makeKeyAndVisible];}];

Faites simplement en tant que root sans aucune présentation:

 [[self window] setRootViewController:controller];

1
C'était exactement mon problème. J'essayais de le présenter avec un UIModalTransitionStyleCrossDissolve, puis de le rendre rootViewController. Après cela, toutes les autres présentations échouaient avec le message d'avertissement donné. Le simple fait de le définir comme rootViewcontroller sans animation a fait l'affaire. Merci!
Bernardo Oliveira

7

Une des solutions à cela est si vous avez childviewcontroller Donc vous présentez simplementviewcontroller sur son parent en donnant

[self.parentViewController presentViewController:viewController animated:YES completion:nil];

Et pour ignorer, utilisez le même contrôleur de rejet.

[self dismissViewControllerAnimated:YES completion:nil];

C'est la solution parfaite qui fonctionne pour moi.


7

Utiliser [self.navigationController presentViewController:xxx animated:YES completion:nil]sous iOS 8.


5

Essayez ce code

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:<your ViewController object>];

[self.view.window.rootViewController presentViewController:navigationController animated:YES completion:nil];

4

Essayez de présenter TabBarControllers'il s'agit d'une TabBarControllerapplication basée.

[self.tabBarController presentViewController:viewController animated:YES completion:nil];

La raison pourrait être un selfenfant de TabBarControlleret vous essayez de présenter à partir d'un fichier ChildViewController.


4

Dans Swift 4.1 et Xcode 9.4.1

La solution est

DispatchQueue.main.async(execute: {
    self.present(alert, animated: true)
})

Si j'écris comme ça, j'obtiens la même erreur

let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: { action in
    })
alert.addAction(defaultAction)

present(alert, animated: true, completion: nil) 

Je reçois la même erreur

Presenting view controllers on detached view controllers is discouraged <MyAppName.ViewController: 0x7fa95560Z070>.

La solution complète est

let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: { action in
     })
alert.addAction(defaultAction)
//Made Changes here    
DispatchQueue.main.async(execute: {
    self.present(alert, animated: true)
})

L'exécuter via DispatchQueue comme celui-ci a fonctionné pour moi. Je fais un performSegue sur un contrôleur de vue modal, appelé depuis viewDidLoad sur mon premier contrôleur de vue (un écran d'introduction de premier lancement pour orienter les nouveaux utilisateurs). Il se chargeait bien, mais générait l'avertissement. L'encapsulation de l'appel performSegue dans l'appel asynchrone DispatchQueue élimine l'avertissement. Merci!
Grant Neufeld

4

Oui, j'ai également rencontré le même message d'avertissement lors de l'affichage d'un contrôleur d'alerte qui était dans une autre vue. Plus tard, j'ai évité cela en présentant le contrôleur d'alerte du contrôleur de vue parent comme ci-dessous:

[self.parentViewController presentViewController:alertController animated:YES completion:nil];

3

vous devez ajouter le contrôleur de vue qui présentera le nouveau contrôleur en tant qu'enfant du contrôleur de vue parent.

Disons que vous avez yourMainViewController, puis vous ajoutez un nouveau contrôleur appelé controllerA, et ensuite vous voulez présenter un nouveau contrôleur appelé controllerB de controllerA

vous devez écrire quelque chose comme ceci:

[self addChildViewController:controllerA]; //self is yourMainViewController
[self.view addsubView:controllerA.view]; 

et dans controllerA vous pouvez présenter le nouveau contrôleur sans avertissement

[self presentViewController:controllerB animated:YES completion:nil]; //self is controllerA

1

Assurez-vous que vous disposez d'un contrôleur de vue racine pour commencer. Vous pouvez l'installer didFinishLaunchingWithOptions.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    [window setRootViewController:viewController];
}

1

Beaucoup de raisons pour cet avertissement. Le mien est parce que j'ai un segue connecté d'un ViewController à un autre qui sera présenté de manière modale. Mais, le ViewController que je présente est généré dynamiquement par un PageViewController. C'est pourquoi il est détaché dans le Storyboard. Mon application ne va pas planter à cause de cela; mais je voudrais faire taire l'avertissement.


1

J'ai atteint sur ce fil où j'ai une barre de navigation personnalisée et j'appelais un AlertViewController à travers elle.

J'ai dû l'ajouter en tant qu'enfant à mon contrôleur de vue principal. Ensuite, je pourrais appeler présent sans aucun avertissement.

Vous devez ajouter votre en Zoomed Image View Controllertant qu'enfant du ViewController principal.

(par exemple)

[self addChildViewController:ZoomedImageViewController];

Ensuite, vous pourrez appeler votre ZoomedImageViewController

[self presentViewController:ZoomedImageViewController];

1

De nombreuses réponses sont justes.

  • Vérifiez que votre PresentViewController a parentViewController ou non.
  • Si non, ajoutez-le quelque part, il devrait être ajouté
  • sinon, vérifiez que parentViewController a parentViewController de manière récursive jusqu'à ce que chaque viewController ait un parent

Ce problème m'est arrivé lorsque mon collègue a ajouté un AViewController à BViewController. D'une manière ou d'une autre, il ajoute simplement la vue AViewController à la vue BViewController.

Corrigé par ajouter bViewController.addChild (aViewController)


1
Merci! l'ajout d'addChild dans mon bloc d'achèvement Hero.shared.transition a complètement résolu mon problème.
landnbloc

0

Cela dépend si vous voulez afficher votre alerte ou quelque chose de similaire dans n'importe quel endroit du genre UIViewController.

Vous pouvez utiliser cet exemple de code:

UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Example" preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil];

[alert addAction:cancelAction];


[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:alert animated:true completion:nil];

Avec votre code, cela ne fonctionne pas et donne ce journalAttempt to present <UIAlertController: 0x7fc01a1eb600> on <ViewController: 0x7fc019821e00> whose view is not in the window hierarchy!
Naveed Abbas
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.