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.
Related
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];
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
I like to segue from the current view controller to the "settings" view controller. Which method is more efficient to transition and why? Thanks! I have to segue in code because I have to observe a condition at run time.
Method 1:
UINavigationController *navigationController = (UINavigationController *)[[[UIApplication sharedApplication] delegate] window].rootViewController;
SettingsViewController *v = [self.storyboard instantiateViewControllerWithIdentifier:#"settings"];
[navigationController pushViewController:v animated:YES];
Method 2: In the storyboard, control-drag the current view controller icon (bottom left) to "settings" view controller and then name the segue identifier "gotoSettingsVC", set style to "push" and then use this code...
[self performSegueWithIdentifier:#"gotoSettingsVC" sender:nil];
Both work just fine.
If you're using storyboards, segues are probably the better way to go. Instantiate the initial view controller and let the segues be your guide.
Also, while Method 1 can be used with storyboards, unlike segues, it also can be used with regular xibs (or no interface builder at all) as well.
Other than that, it comes down to preference. Some people hate the interface builder and others swear by it. ;)
I've done a bit of research and read other answers I found here but haven't found anything that actually works. I have an app that when something is posted I want to go to the post and if the back button is pressed when viewing the post it should go back two views basically skip over the compose view.
Below is what I've tried but it gives
Warning: Attempt to present on whose view is not in the window hierarchy!
-(IBAction)post{
[[self presentingViewController] dismissModalViewControllerAnimated:NO];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
ViewPostViewController *dvController = [[ViewPostViewController alloc] initWithNibName:#"ViewPostViewController" bundle:[NSBundle mainBundle]];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:dvController];
nc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:nc animated:NO];
[nc release];
}
If you trying to create a hierarchy of views like this, you should probably be using these:
[self.navigationController pushViewController:ViewController animated:BOOL completion:nil];
[self.navigationController popViewControllerAnimated:BOOL completion:nil];
Instead of:
[self presentViewController:ViewController animated:BOOL completion:nil];
[self dimissViewControllerAnimated:BOOL completion:nil];
PresentViewController is usually used to show a single view controller then dismiss it, not generally when you want to show several view controllers in a chain, then work your way back up the chain.
The former is advisable because it uses the stack concept to push and pop view controllers. So you can start with your initial list set up as the root view controller, push on your post compose view, then push on the third view to go to posting. Then when you want to go back to the first view controller by popping off two view controllers, you can use:
[self popToRootViewControllerAnimated:BOOL completion:nil];
You might find the UINavigationController reference useful.
Good luck.
If you want to present a view controller right after another modal view controller has animated out then you have to delay it because otherwise the new one will not appear.
before iOS 5 you would do a performSelectorAfterDelay: with something like 0.25 sec. For iOS 5 and above you wouldn't use modelViewController methods any more as those have been deprecated. Instead you use the presentViewController methods which give you an completion block that is called when the animation is done.
I'm a little confused about what you're trying to do. If you're using a navigation controller, you should be doing pushes and pops, not presenting and dismissing. If you want to use navigation controllers, then you can use popToViewController:animated: to go back to any particular controller without passing through the ones in between. You would have to create a custom back button, though, or do it in code, because the standard back button will only take you back to the previous view controller.
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.