Warning :-Presenting view controllers on detached view controllers is discouraged
Asked Answered
A

19

196

In my app, I am using a navigation controller. Later on in some view I am using presentViewController for showing a zoomed image. Also I am not using a Storyboard or nib.

I am getting this error in iOS 7 only. It works fine in iOS 6 and earlier:

Presenting view controllers on detached view controllers is discouraged

Aran answered 10/11, 2013 at 14:19 Comment(6)
I did not figure out yet. But in my app i am not assigning any viewcontroller to window.rootviewcontroller. i am adding view to window. May be that is the reason for me. but not sure...Aran
@GaganJoshi The reason you mentioned above might not be the cause. Even I am facing the same issue. And in our project I am assigning a view controller to window.rootviewcontroller .Shoon
I think the other comments correctly relate this to something about the rootViewController and the window connection. I haven't quite figured that out, but I have been able to work around the issue by presenting the controller directly on the rootViewController instead of on the navigation controller or one of its children.Broaden
Azaxis got it: https://mcmap.net/q/127888/-warning-presenting-view-controllers-on-detached-view-controllers-is-discouragedEarthnut
Hmm - I'm getting this also, but for now I'm going to ignore it. My app is a drawing app. In the app, it is possible to edit curves. Editing a curve is done by presenting a curve editor as a formsheet. The curve editor meanwhile, allows the user to pick a color. I have a color picker for that. Part of the color picker is a view that shows a selection of the presenting viewcontroller. It makes no sense to get that detail from the main view controller. The relevant section is on the curve edit view controller. I note that it says discouraged - not forbidden.Eos
I tried changing my code to present from the presenting VC, and the app crashed - so I'm leaving as is. :)Eos
A
7

One of the solution to this is if you have childviewcontroller So you simply presentviewcontroller on its parent by given

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

And for dismiss use the same dismissview controller.

[self dismissViewControllerAnimated:YES completion:nil];

This is perfect solution works for me.

Aran answered 8/10, 2014 at 11:31 Comment(0)
A
211

To avoid getting the warning in a push navigation, you can directly use :

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

And then in your modal view controller, when everything is finished, you can just call :

[self dismissViewControllerAnimated:YES completion:nil];

Astylar answered 8/1, 2014 at 18:44 Comment(8)
I am presenting image picker with this line code "[self.view.window.rootViewController presentViewController:viewController animated:YES completion:nil];" But unable dismiss poicker view with this line "[self dismissViewControllerAnimated:YES completion:nil];" Any alternate option for dismisscontrollerUsage
@keyurbhalodiya You need to call the dismissViewController method from the modalView to make it work. So if you displayed a view named viewB from a viewA with [viewA.window.rootViewController presentViewController:viewB], in viewB you need to add a button for example, associated to a custom action calling [self dismissViewControllerAnimated]. Is it clearer ?Astylar
It drops me a new warning "Warning: Attempt to present <UpgradeViewController: 0xdfcae10> on <WelcomeViewController: 0xdf654d0> whose view is not in the window hierarchy!", but now this is processing the events (before, the modal controller was unresponsive)Encomium
Not presenting viewcontroller in iOS 8.Marileemarilin
for iOS 8: [self.view.window.rootViewController.navigationControllerFridell
If it's not presenting in iOS8, then go to your AppDelegate and set window rootViewController like that self.window.rootViewController = myViewController;Intravenous
using self.navigationController did it for me.Ceiling
This code solve the problem in Swift with a MFMailComposeViewController: self.view.window!.rootViewController!.presentViewController(mailComposeViewController, animated: true,completion: nil)Brower
S
83

Wait for viewDidAppear():

This error can also arise if you are trying to present view controller before view actually did appear, for example presenting view in viewWillAppear() or earlier. Try to present another view after viewDidAppear() or inside of it.

Snakebite answered 7/8, 2015 at 12:28 Comment(4)
In other words, don't present any view controllers in viewDidLoad(), people! I've made this mistake so many times...Depress
Thanks this helped. I had code in viewDidLoad where it tried to display a dialog at the end.Carmelitacarmelite
I'm getting this error when running unit/integration tests where I don't test with animations.Krug
Yeah, that was exactly as you said! If not this message, I would likely had to refactor.Purchasable
A
64

The reason of this warning is i was presenting a view controller over a small view that is not full size view. Given below is the image of my project. where on click on four option above. User navigate to different childviewcontroller's view.(it works like tabViewcontroller). But the childviewcontroller contains view of small size. So if we present a view from childviewcontroller it gives this warning.

master detail view

And to avoid this, you can present a view on childviewcontroller's parent

  [self.parentViewController presentViewController:viewController animated:YES completion:nil];
Aran answered 7/8, 2014 at 12:38 Comment(2)
[self.view.window.rootViewController.navigationController pushViewController:YOUR_VIEW_CONTROLER animated:YES];Fridell
"was presenting a view controller over a small view that is not full size view." ... EXACTLY. Nice work.Cati
A
23

In my case, I've a sampleViewController's view added as a subview, then tries to present a popover from the view of sampleViewController (here self instead a UIViewController instance):

[self.view addSubview:sampleViewController.view];

The right way should be below:

// 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];

B.t.w., this also works for the case that present a popover form a tableview cell, you just need to make sure the tableview controller has been added as child view controller as well.

Ahl answered 23/2, 2015 at 10:57 Comment(4)
In addition call didMoveToParentViewController. Pleaes check out Add and Remove ChildViewController: gist.github.com/tomohisa/2897676Makeyevka
@jianzong i remember there's no need to do the last step. Any way, let me add it, thx for the suggestion. :)Ahl
Yes it will work without the last step. I think the purpose of it is to inform the parentViewController so that it will call some methods to do something. :)Makeyevka
it works for me, im using the one controllers view in another controller - (Container view programatically), i didn't add [self addChildViewController:sampleViewController];, now i added this, thank youReareace
P
20

Swift 3

For anyone stumbling on this, here is the swift answer.

self.parent?.present(viewController, animated: true, completion: nil)
Pandect answered 12/5, 2017 at 1:0 Comment(0)
L
15

I think that the problem is that you do not have a proper view controller hierarchy. Set the rootviewcontroller of the app and then show new views by pushing or presenting new view controllers on them. Let each view controller manage their views. Only container view controllers, like the tabbarviewcontroller, should ever add other view controllers views to their own views. Read the view controllers programming guide to learn more on how to use view controllers properly. https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/

Laterite answered 10/12, 2013 at 5:12 Comment(0)
V
9

I have almost the same problem. The reason was that I tried to present "some" controller on another and after animation was completed I was setting presented controller as root. After this operation all further controllers presenting bring me to the warning: "Presenting view controllers on detached view controllers is discouraged". And I solve this warning just settings "some" controller as root without any presentation at the begin.

Removed:

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

       [self window].rootViewController = controller;

       [[self window] makeKeyAndVisible];}];

Just make as root without any presentation:

 [[self window] setRootViewController:controller];
Vendible answered 27/6, 2014 at 9:57 Comment(1)
This was exactly my problem. Was trying to present it with a UIModalTransitionStyleCrossDissolve and then make it rootViewController. After that, all other presentations were failing with the given warning message. Just setting it as rootViewcontroller without animation did the trick. Thanks!Chamonix
A
9

In Swift 4.1 and Xcode 9.4.1

The solution is

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

I wrote like this, i'm getting same error

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) 

I'm getting same error

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

Complete solution is

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)
})
Akan answered 28/8, 2018 at 5:37 Comment(1)
Running it through DispatchQueue like this worked for me. I’m doing a performSegue to a modal view controller, called from the viewDidLoad on my first view controller (a first-launch intro screen to orient new users). It was loading fine, but generating the warning. Wrapping the performSegue call in the DispatchQueue async call eliminates the warning. Thanks!Pressure
A
7

One of the solution to this is if you have childviewcontroller So you simply presentviewcontroller on its parent by given

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

And for dismiss use the same dismissview controller.

[self dismissViewControllerAnimated:YES completion:nil];

This is perfect solution works for me.

Aran answered 8/10, 2014 at 11:31 Comment(0)
C
7

Use [self.navigationController presentViewController:xxx animated:YES completion:nil] in iOS 8.

Confucianism answered 14/10, 2014 at 9:5 Comment(0)
M
5

Try this code

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

[self.view.window.rootViewController presentViewController:navigationController animated:YES completion:nil];
Myongmyopia answered 11/3, 2016 at 17:1 Comment(0)
D
4

Yes, I also faced the same warning message while displaying an Alert controller which was in another view. Later on I avoided this by presenting the alert controller from the parent view controller as below:

[self.parentViewController presentViewController:alertController animated:YES completion:nil];
Daiquiri answered 27/5, 2015 at 5:47 Comment(0)
C
4

Try presenting on TabBarController if it is a TabBarController based app .

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

Reason could be self is child of TabBarController and you are trying to present from a ChildViewController.

Contradance answered 22/8, 2017 at 10:7 Comment(0)
S
3

you need to add the view controller that will present the new controller as a child of the parent view controller.

Let's say you have yourMainViewController, then you add a new controller called controllerA, and then you want to present a new controller called controllerB from controllerA

you have to write something like this:

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

and within controllerA you can present the new controller without warnings

[self presentViewController:controllerB animated:YES completion:nil]; //self is controllerA
Supercolumnar answered 21/8, 2015 at 17:59 Comment(0)
P
2

I reached on this thread where I have a Custom Navigation Bar and I was calling an AlertViewController through it.

I had to add it as a child to my main view controller. Then I could call present it without any warning.

You should add your Zoomed Image View Controller as a child of the main ViewController.

(e.g)

[self addChildViewController:ZoomedImageViewController];

Then you'd be able to call your ZoomedImageViewController

[self presentViewController:ZoomedImageViewController];
Punt answered 30/7, 2019 at 10:0 Comment(0)
Z
1

Make sure you have a root view controller to start with. You can set it in didFinishLaunchingWithOptions.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    [window setRootViewController:viewController];
}
Zanezaneski answered 14/4, 2014 at 13:55 Comment(0)
W
1

Lots of reasons for this warning. Mine is because I have a segue connected from a ViewController to another that will be presented modally. But, the ViewController I am presenting from is being dynamically generated by a PageViewController. Which is why it is detached in the Storyboard. My app isn't going to crash because of it; but I would like to silence the warning.

Watchband answered 27/4, 2016 at 15:1 Comment(0)
V
1

It depends if you want to show your alert or something similar in anywhere of kind UIViewController.

You can use this code example:

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];
Veats answered 4/3, 2018 at 1:20 Comment(1)
With your code, it doesn't work and gives this log Attempt to present <UIAlertController: 0x7fc01a1eb600> on <ViewController: 0x7fc019821e00> whose view is not in the window hierarchy!Punt
C
1

Many answers are right.

  • Check your presentingViewController have parentViewController or not.
  • If no, add it to somewhere it should be added
  • else, check it's parentViewController has parentViewController recursively until every viewController has parent

This issue happened to me when my co-worker add a AViewController to BViewController. Somehow, he just add the AViewController's view to BViewController's view.

Fixed by add bViewController.addChild(aViewController)

Chicanery answered 7/2, 2020 at 6:21 Comment(1)
Thank you! adding addChild in my Hero.shared.transition completion block completely solved my problem.Yogini

© 2022 - 2024 — McMap. All rights reserved.