Calling pushViewController after a presentViewController does not work - ios

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

Related

Push to a UIViewController from a presentViewController

I want to push to a new viewcontroller from a presented viewcontroller. I don't want to dismiss the presented viewcontroller. I want the new viewcontroller to come over the presented viewcontroller.
Can anybody tell me how to do that.
You do not push form the presented controller so the best option is
First you have to dismiss the controller without animation and then in the method of -(void)viewWillApper you can easly push to the controller where you want to push.
You can do by using UINavigationController like
UINavigationController *vcObject = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"YourViewController"];
[self.navigationController presentViewController:vcObject animated:YES completion:NULL];
Now you can easily push or pop to other ViewController like you want.Thankyou
Open your modalView controller as a new rootViewController :
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:nextViewController];
[self.navigationController presentViewController: navController animated:YES completion:nil]

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...

Application tried to present modally an active controller: uinavigationcontroller

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)
}

pushing a viewcontroller from the right, dynamic navigation controller

I have a UIView with a button on it, when the user clicks that button I have another xib file that I want to push. Now this view doesn't have a navigationcontroller so I create one dynamically like so :
myVC *viewController = [[myVC alloc] init];
UINavigationController *controller = [[UINavigationController alloc]
initWithRootViewController:viewController];
[self presentModalViewController: controller animated: YES];
But this is modal, how can I have myV be pushed from the right?
Thanks.
Try this following format. This will help you.
myVC *viewController = [[myVC alloc] initWithNibName:"myVC" bundle:nil];
viewController.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
[[self navigationcontroller] presentModalViewController:viewController animated: YES];
This code is used to flip the new view controller horizondally.
Please refer "UIModalTransitionStyle".
You need edit your appdelegate class and there itself you need to assign your view controller to navigation controller as a root class. Then it will automatically convert all yours view controller to navigation view. Try it out
Initialize View controller
Intitilize UINavigationController root as 1st step view controller
present the nav
Here is the code
let vc = SomeViewController()
let nav = UINavigationController(rootViewController: con)
present(nav, animated: true, completion: nil)

What exactly does presentModalViewController do?

If I have a navigationController which is init with a root view controller MyViewController's instance.
And in that MyViewController's code
I can use
AnotherViewController *vc = [[AnotherViewController alloc] init];
[self presentModalViewController:vc animated:YES];
or
AnotherViewController *vc = [[AnotherViewController alloc] init];
[self.navigationController presentModalViewController:vc animated:YES];
I found these two works the same. Both present the modal view correctly.And I have found that the presented AnotherViewController's "parentViewController" property are all set to the navigation controller.
Why would this happen?the presentModalViewController automatically detect that the self is the subview of the navigation controller and re send the message to navigation controller?
Because MyViewController is the root view controller of the UINavigationController, it gets the convenience method of presentModalViewController: animated: by default. So when you say self.navigationController, it is referring to the same navigationController that presentModalViewController gives you. I think Apple is just trying to make it more intuitive to use the convenience method.

Resources