Can't push viewController because navigationController is nil - ios

I can't perform a transition between viewControllers because navigationController is nil. I have logged navigationController in different parts of the class but it returns nil everywhere. In storyboard the viewController is embedded in a navigationController. I have checked other threads on SO with the same issue, but none of the answer has helped or even really made sense to me.
Can't push because self.navigationController is nil
navigationController is nil,when push the viewcontroller
Why is it nil? And how do I solve this? An error message is also returned:
I have tried both using a segue:
[self.navigationController performSegueWithIdentifier:#"experienceDetails" sender:self];
as well as pushing:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Inspiration" bundle:nil];
ExperienceViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"experience"];
[self.navigationController pushViewController:viewController animated:NO];
Nothing happens using push but an error message is produced:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior
I have looked for solutions on that error too, but there doesn't seem to be a clear and concrete answer to how to solve it. Again, those suggestions I read and tried didn't work.
I'm really at a loss here. Such a simple thing to do but I'm hindered by something I don't even understand.
EDIT
If it helps, I have a tab bar and in one item I have the viewController that is embedded in a navigationController and from there I want to push to another viewController within the same storyboard.
EDIT
I got this to work:
[self showViewController:viewController sender:self];
very likely because it doesn't use navigationController. Its presented as modular though and is not part of the navigation stack, which is not something I want. Just good to know that things would work if navigationController wasn't nil.

I reproduced the described behaviour when segue experienceDetails was created from navigation controller... and here is solution
1) deleted segue experienceDetails form navigation controller
2) created push segue with identifier experienceDetails from included view controller to detail view controller
3) in view controller performed segue from self as below
[self performSegueWithIdentifier:#"experienceDetails" sender:self];

Related

pushViewController not working read much online though

Here is what code I get online, and it does work on previous project that I develop.But in this project, self.navigationController is null when I NSLog it, and guys online talked about add some code in delegate file, but I found nothing in previous project also I am not very clear what code should I add there. Anyone can give me a hand?
UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewCollection"];
[self.navigationController pushViewController:next animated:YES];
If UINavigationController is nil, it means that the view controller that you use (which you instantiate from a Storyboard) is not actually embedded within a UINavigationController.
In order to embed it into a UINavigationController, you need to drag and drop a UINavigationController into your Storyboard and then ctrl-drag from the UINavigationController to your custom view controller and set it as the rootViewcontroller of the UINavigationController.
as nburk said: you need a NavigationController with a RootViewController which is your view. and dont forget to set the NavigationController as Initial View Controller. This all is done in the IB.

avoid navigation transition error with view controller identifier

I'm using Xcode 5 with storyboards and I should do something like this:
ViewController with a Start button that launches IntermediateViewController
IntermediateViewController that does an activity and then returns the value to the ViewController.
For the passage ViewController->IntermediateViewController I've set the start button to trigger a push segue. Actions are done and this part seems ok.
Now I have to go back to ViewController passing a string I got in IntermediateViewController methods.
If I use:
ViewController *viewController=[self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"viewController"];
viewController.passedString=_mystring;
[self.navigationController pushViewController:viewController animated:NO];
I get this error:
"Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted"
Is there a way to retrieve my viewController first instance through its identifier or any other solution that will lead the app back to viewController setting also its variable?
Thanks in advance
Even if it's not the exact answer to the question, I solved using this steps:
1) checked in storyboards that each element triggers only one action or segue, not both.
2) calling second view using:
[self.navigationController pushViewController:intermediateViewController animated:NO];
2) going back to previous view.
[self.navigationController popViewControllerAnimated:YES];
3) doing this before popping:
pass string between controllers

After using presentViewController existing segues can't be found

I am returning to my login view using the code below. The view loads correctly and everything looks fine. All buttons work etc.
JALoginViewController *loginVC = [[JALoginViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:loginVC];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self.navigationController presentModalViewController:navigationController animated:YES];
However, if a user tries to log in again, the segue that takes them to the next scene can't be found.
I'm using performSegueWithIdentifier if the users login credentials are correct, like this:
[self performSegueWithIdentifier:#"loginSegue" sender:self];
This is the error I receive:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<JALoginViewController: 0x8d614b0>) has no segue with identifier 'loginSegue''
I've done lots of searching on Google and through the docs for the solution to this, the closet I've got (at least I think) is this question. The explain and solution sound like they could be correct and relevant, but I can't put them into practice.
Documents I've read and tried:
initWithRootViewController
popToRootViewController - The current root view controller is for a tab bar - not the login scene I need so as far as I'm aware I can't use this.
popViewControl
pushViewControl - This works to an effect, I don't think it is the correct way though. I don't want there to be navigation bar and I don't want my tab bar to appear when the user returns to the login scene.
I've tried various methods with limited / no effect. At this stage any help would be greatly appreciated.
Please let me know if I haven't provided enough information.
Thanks
JA
Edit - Zoomed out image of storyboard
![Zoomed out image of Storyboard][1]
On the basis of the screen snapshot of the revised question, from your rightmost red-highlighted scene, you should be able to:
[self.tabBarController dismissViewControllerAnimated:YES completion:nil];
and you'll be back at that initial screen (I'm assuming you did modal segue from initial screen to your tab bar controller).
Original answer:
If you want to manually push to a view controller, rather than creating it via alloc/init, you should use
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"loginsSceneStoryboardIdHere"];
or, if that view controller was the "initial" scene (the one with the simple arrow coming in from the left), you could use
UIViewController *controller = [self.storyboard instantiateInitialViewController];
And you shouldn't be manually creating the navigation controller, either. If the loginVC needs a navigation controller, you should embed that scene in a navigation controller right in the Interface Builder, then give that new navigation controller its own unique storyboard identifier, and then you can
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"navControllerSceneStoryboardIdHere"];
[self presentViewController:controller animated:YES completion:nil];
I must confess that I'm worried by this whole "return to login via pushViewController" construct. I assume you know that you're not "returning" to it, but creating a new copy of it. If you push/modal from A to B and then B to C and then C to A, you're holding 4 views and their controllers in memory, two copies of A and one of B and one of C (which is, obviously, not good). I just wanted to make sure you don't have a circular set of segues and/or push/presentViewController references.
If the login is the initial scene in your app and if you've been doing only push segues (no modal segues along the way), you can do a:
[self.navigationController popToRootViewControllerAnimated:YES];
That will take you to the top level view controller, and it will pop off and release all of the intervening scenes.
If you're using iOS 6, you can avail yourself of the unwind segue, which can achieve the same functionality, but it doesn't care whether the preceding segues were pushes or modals.
There are lots of ways of skinning the cat, but generally doing a new presentViewController to the first scene in your storyboard is a very bad idea.

Push UIViewController after popping to root view

I have an app using storyboards and a navigation controller. At some stage in my app flow I get about four views on the view stack at which point I must pop off all the views until I get to the root view. After which I need to manually push another view.
I've tried various things with no luck. I've tried to use the built-in API call:
[self.navigationController popToRootViewControllerAnimated:YES];
At which point I try to call a push segue by referencing the root view and calling the segue method.
RootView *obj = [[RootView alloc] init];
[obj callSegue];
Or
[self.navigationController performSegueWithIdentifier:#"pushView" sender:self];
At any rate, I'm completely stumped on this one. Can anyone help?
UPDATE:
Thanks for the replies everyone. I was digging around some more and found a solution, one of many I'm sure.
// Reference to navigation controller. Apparently if you use self.navigationController in a popToRootViewController call it sets self.navigationController to nil.
UINavigationController *navigationController = self.navigationController;
[navigationController popToRootViewControllerAnimated:NO];
// Reference to view to push - must set storyboard ID in inspector
ViewToPush *viewRef = (ViewToPush *)[self.storyboard instantiateViewControllerWithIdentifier:#"gameView"];
[navigationController pushViewController:gameView animated:NO];
How about this?
[self.navigationController setViewControllers:#[rootViewController, viewControllerTwo] animated:YES];
This sets your stack to your root and a new controller and push animations are used. If you need a quick'n dirty reference for rootViewController, you can use [[self.navigationController viewControllers] objectAtIndex:0].
A good solution to this is to use an "Unwind Segue". Basically, an unwind segue is a segue that takes you back down the stack of pushed controllers, and then performs an IBAction method in the destination controller. What you want to do is make an unwind segue from your current controller to the root, and then put a performSegueWithIdentifier: call in the called method.
Here's a tutorial on unwind segues: Tutorial
Meybe you can try this :
Set a flag (a #property) in your root VC, something like shouldPushAutomatically
In the VC where you call [self.navigationController popToRootViewControllerAnimated:YES];, implement the prepareForSegue:WithIdentifier: method. In this method, use (MyVC*)segue.destinationViewController to access your root VC and set yourflag to YES.
In the viewDidLoad of your root VC, try calling your push segue (in fact, you may need to call it in viewDidAppear).
Not sure if this will work, but that's the way I would try to make it work.

Moving from one view to another iOS

I'm relatively new to iOS development. I am to move from one viewController to another I use a modal segue transition on button click. This is a game so i want to allow the user to click images to essential move the the app menus.
I have a main page that displays several images, on clicking one i want to be able to move to another view. Currently doing this with a modal segue is causing odd problems with my touchesEnded event where if, for example, i navigate to a page 3 times the touchesEnded event is fired 3 times.
Is there a better way for me to do this or am i just missing thing fundamental?
Thanks
Yes, I think you must make the the navigation controller your root view controller then push views accordingly
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:YOUR_BASE_CONTROLLER]
self.rootViewController = nav;
this is in your app delegate.
Then in your action method
[self.navigationController pushViewController:secondViewController animated:YES]
Im assuming you are using the Storyboard to link VCs using segues.
Modal segues are great for simple transitions but really seem to limit what you can accomplish when they are just linked through SB. Ive found that creating an IBAction that includes the following for a VC segue will allow you to not only control your segues more efficiently but also allow you to have a clearer view of what is actually occurring during the transition.
-(IBAction)goToVc:(id)sender{
//Other code to take place during the segue here
//This will identify the Storyboard in use
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
//This will identify the View Controller to switch to
SecondViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewControllerID" ];
[self presentViewController:vc2 animated:YES completion:NULL];
}
Every time you perform a modal segue, you lose the UINavigationController that you were previously using. What you need to do is embed a UINavigationController in the view that you are performing a modal segue to.
Check out a question I answered the other day to help you visualize more clearly what I'm talking about.

Resources