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

Gagan Joshi
la source
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];

cdescours
la source
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.

Azaxis
la source
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];
Gagan Joshi
la source
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.

Kjuly
la source
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/

Daniel Ytterbrink
la source
15

Swift 3

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

self.parent?.present(viewController, animated: true, completion: nil)
Jérémie
la source
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];
Averem
la source
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.

Gagan Joshi
la source
7

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

Tao Fang
la source
5

Essayez ce code

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

[self.view.window.rootViewController presentViewController:navigationController animated:YES completion:nil];
Vlad
la source
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.

Warif Akhand Rishi
la source
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)
})
iOS
la source
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];
Sivasagar Palakurthy
la source
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
Chuy47
la source
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];
}
samwize
la source
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.

Lee Probert
la source
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];
Naveed Abbas
la source
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)

Jérôme Li
la source
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];
Fabio
la source
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