Application tried to present modally an active controller: uinavigationcontroller - ios

I'm running into an issue where an error is only raised periodically. In fact it seems almost random. Here's what happens, I'm launching a modal view controller with the following code:
- (void)createMessageClicked
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Channel" bundle:nil];
UINavigationController *nav = [sb instantiateViewControllerWithIdentifier:#"HIComposeMessageNavController"];
HIComposeMessageViewController *vc = [[nav viewControllers]objectAtIndex:0];
vc.channel = [self.channels objectAtIndex:0];
[self.navigationController presentViewController:nav animated:YES completion:nil];
}
Most of the time, this works fine. However once in a while the app crashes and raises the error "Application tried to present modally an active controller <UINavigationController>. Any ideas what I'm doing wrong here?

Try instantiating the controller that is embedded in your navigation controller in your storyboard, then create a new instance of a generic navigation controller:
HICompseController *controller = [sb instantiateViewController:
HIComposeMessageViewController];
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:controller];
[self presentViewController:nav animated:YES completion:nil];

I would suggest setting an ivar for your UINavigationController, because every time the action is triggered you are creating a whole new navigation controller and present it modally.
I suspect it occurs more often when the time in between click actions are close, hence after the modal controller has dismissed but did not get enough time for navigation controller to be deallocated before a new one that is instantiated from the same class is created and presented again modally. By using the same navigation controller, you can at least be sure that it is dismissed before it is presented again via that method.
Try to create an ivar for the navigation controller and reuse that every time in that method.

This worked for me:
if let presented = self.presentedViewController,
!presented.isBeingPresented {
self.present(navController, animated: true, completion: nil)
}

Related

IOS/Objective-C/Storyboard: Prevent ViewController From Launching Modally

I want a viewcontroller to launch using a show transition, not modally from the bottom. Normally when I use the following code that's what happens. However, in this case, it is launching as a modal controller from the bottom up. Is there a switch I don't know about or could something be set in Storyboard that is causing this VC to launch modally from the bottom instead of showing?
UIStoryboard *storyBoard = self.storyboard;
IDImportEventsOnboard *importEvents =
[storyBoard instantiateViewControllerWithIdentifier:#"importEventsOnboard"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController: importEvents];
[self presentViewController:nav animated:YES completion: nil];
The VC is embedded in a navigation controller.
Should I be using showViewController directly to the targetVC without going through the Nav? Or a pushViewController What is a proper, robust way to show a VC with a show transition?
Thanks in advance for any suggestions.
In the above code you are 'presenting' a new NavigationController from a ViewController. In order to do a push/show transition, that needs to be done on an instance of a NavigationController. If your current ViewController is already in a NavigationController, you can push the new ViewController onto the current NavigationController stack. For Example:
UIStoryboard *storyBoard = self.storyboard;
IDImportEventsOnboard *importEventsVC =
[storyBoard instantiateViewControllerWithIdentifier:#"importEventsOnboard"];
[self.navigationController pushViewController:importEventsVC animated:YES];

Calling pushViewController after a presentViewController does not work

I am presenting my view controller like this -
[self.navigationController presentViewController:self.thingContainerViewController animated:YES completion:nil]; //self.navigationController not nil here
This shows a UITableView. I want to push a VC on the navigation stack from here. But the self.navigationController is nil at this point. Any idea how to make this work?
[self.navigationController pushViewController:otherContainer animated:YES]; //self.navigationController is nil at this point
You need to wrap the view controller you are presenting in a navigation controller in order to be able to use the push and pop methods.
So for the first step:
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.thingContainerViewController];
Then:
[self.navigationController presentViewController:navigationController animated:YES completion:nil];
If you do that, your code will work.
Swift 3/Swift 4
first of all you need to set navigation controller on which you want to present.After that do navigation process on your second view controller.
Example like that
let firstPresentVC = FirstVC(nibName:"FirstVC",bundle:nil)
let navVC = UINavigationController(rootViewController:firstPresentVC)
navVC.isNavigationBarHidden = true
self.present(navVC, animated: true, completion:nil)
Now You are on Present Stack With Navigation
You can push after that
let secondPushVC = secondPushVC(nibName:"secondPushVC",bundle:nil)
self.navigationController?.pushViewController(secondPushVC, animated: true)
UIViewController.navigationController means:
The nearest ancestor in the view controller hierarchy that is a navigation controller.
on the other hand, presentViewController makes new view controller out of hierarchy, the new view controller has no navigation controller ancestor unless you assign one to it by [[UINavigationController alloc] initWithRootViewController:self.thingContainerViewController] as #Dima mensioned.
so, the solution is
UINavigationController *targetVCWithNavigationControllerAncestor = [[UINavigationController alloc] initWithRootViewController:self.thingContainerViewController];
[self.navigationController presentViewController:targetVCWithNavigationControllerAncestor animated:YES completion:nil];
then you can push new view controller from self.thingContainerViewController

ios - Nothing happens when pushing view controller to navigation controller

I am trying to push a view controller to a navigation controller, but nothing happens.
I have the following code in my appDelegate (which works fine it seems):
ViewController1* VC1 = [[ViewController1 alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:VC1];
self.window.rootViewController = navController;
And the following code in VC1:
ViewController2 *VC2 = [[ViewController2 alloc]init];
[self.navigationController pushViewController:VC2 animated:YES];
VC2 gets initialised but isn't pushed to the navigation controller and therefor never presented. I have tried looking for answers for a while now but without success. What am I missing?
Thanks in advance!
Turns out the problem was that another navigation controller was active and had to be dismissed. The code posted was correct and all but I had totally missed another navigation controller.

Instantiate UINavigationController on button action

I have simple application with only one main view, which has 'Settings' button, and settings are tree-grouped, so I wand to present them in navigation controller. And I don't want navigationController in main view, because I don't want navigation bar there.
That's why I don't instantiate navigationController in application: didFinishLaunchingWithOptions:. And when I check self.navigationController in 'Settings' button handler, it returns nil.
So I wrote this: (I use ARC)
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[self.view.window setRootViewController:navigationController];
[navigationController pushViewController:settingsViewController animated:YES];
}
This works, although it pushes settingsViewController without animation (don't know why).
Is this generally the correct way to do - to change rootViewController in the middle of running app?
And if yes - than when I'm done with Settings, I probably need to set rootViewController back to current viewController, as it was before I tapped 'Settings'?
I think you want to create a navigation controller that you will present modally; the following will do:
SettingsViewController* settingsViewController = [[SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:settingsViewController animated:YES];
[self presentViewController: navigationController animated: YES completion:nil];
where self here is the view controller you want to trigger the modal view controller from.
since you present modally the navigation controller you can dismiss it within the code source of your settingsViewController by accessing its navigation controller:
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
To answer your question setting the rootViewController is not the correct way. Present the new vc modally through the presentViewController method.
A better way is to build the navigation vc and present it over your main vc (not replace your main vc).
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: settingsViewController];
[self presentViewController: navigationController animated:YES completion:^{}];
}
Your main vc might realize (maybe as a delegate) that the settings flow is complete. It can then dismiss the presented navigation controller with:
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
Alternatively, the setting flow could dismiss itself...
// somewhere in the settings vc or a vc it pushes, when we decide settings are done
self.navigationController dismissViewControllerAnimated:YES completion:^{}];
Have navigation in main view and have below line (which will hide navigation bar)
[[self navigationController] setNavigationBarHidden:YES animated:YES];
(I would say have this in viewWillAppear and viewDidLoad both, BUT in viewWillAppear is MUST).
Now in second view, to show navigation bar
[[self navigationController] setNavigationBarHidden:NO animated:YES];
Hope this will solve your problem...

iOS presented UINavigationController gets dismissed after it performs a popViewController

In my app i present a UINavigationController modally with a UIViewController as its rootViewController. I do it in form style. I added a second UIViewController which is also in form style and i can push to it fine. However when i perform a popViewController action after the second UIViewcontroller gets popped onto the first, the whole modally presented UIViewController gets dismissed. However i don't perform any dismissing and the dismissing function doesn't get triggered by accident either.
Any ideas why it's happening?
Sincerely,
Zoli
EDIT:
That's how i'm presenting the modal viewcontrollers with a navcontroller:
if(!welcomeScreenAlreadyPresented) {
welcomeScreenViewController = [[WAWelcomeViewController alloc]init];
}
welcomeScreenNavController = [[UINavigationController alloc]initWithRootViewController:welcomeScreenViewController];
[welcomeScreenNavController setModalTransitionStyle: UIModalTransitionStyleCrossDissolve];
[welcomeScreenNavController setModalPresentationStyle:UIModalPresentationFormSheet];
[welcomeScreenNavController setNavigationBarHidden:YES animated:NO];
[self.navigationController presentViewController:welcomeScreenNavController animated:YES completion:nil];
That's how i'm navigation in WAWelcomeViewController.m
registerViewController = [[WARegisterViewController alloc]init];
[self.navigationController pushViewController:registerViewController animated:YES];
And in WARegisterViewController.m that's how i pop back
[self.navigationController popViewControllerAnimated:YES];
What you need to do is put the viewController you want to push inside another UINavigationController.
registerViewController = [[WARegisterViewController alloc]init];
UINavigationController *modalNavigationController = [[UINavigationController alloc] initWithRootViewController:registerViewController]; // autorelease if you are not using ARC
[self presentViewController:navController animated:YES completion:^{}];
You might want to add the modalNavigationController as a property to later call popViewControllerAnimated: on it.

Resources