From Home view - my RootViewController - I open up 2 ViewControllers one after another as user progresses in navigation hierarchy like so:
1) SecondViewController is pushed by button connected in my Storyboard
2) ThirdViewController is presented modally
[self performSegueWithIdentifier:#"NextViewController" sender:nil];
So, the picture is: RootViewController -> SecondViewController -> ThirdViewController
Now in my ThirdViewController I want to have a button to go back 2 times to my RootViewController, i.e. go home. But this does not work:
[self.navigationController popToRootViewControllerAnimated:YES];
Only this guy goes back once to SecondViewController
[self.navigationController popViewControllerAnimated:YES];
How can I remove both modal and pushed view controllers at the same time?
I had a similar situation, where I had a number of view controllers pushed onto the navigation controller stack, and then the last view was presented modally. On the modal screen, I have a Cancel button that goes back to the root view controller.
In the modal view controller, I have an action that is triggered when the Cancel button is tapped:
- (IBAction)cancel:(id)sender
{
[self.delegate modalViewControllerDidCancel];
}
In the header of this modal view controller, I declare a protocol:
#protocol ModalViewControllerDelegate
- (void)modalViewControllerDidCancel;
#end
And then the last view controller in the navigation stack (the one that presented the modal view) should implement the ModalViewControllerDelegate protocol:
- (void)modalViewControllerDidCancel
{
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController popToRootViewControllerAnimated:YES];
}
This method above is the important part. It gets the presenting view controller to dismiss the modal view, and then it pops back to the root view controller. Note that I pass NO to dismissViewControllerAnimated: and YES to popToRootViewControllerAnimated: to get a smoother animation from modal view to root view.
I had the same requirement but was using custom segues between the view controllers. I came across with the concept of "Unwind Segue" which I think came with iOS6. If you are targeting iOS6 and above these links might help:
What are Unwind segues for and how do you use them?
http://chrisrisner.com/Unwinding-with-iOS-and-Storyboards
Thanks.
Assuming your AppDelegate is called AppDelegate, then you can do the following which will reset the rootviewcontroller for the app window as the view RootViewController
AppDelegate *appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
RootViewController *rootView = [[RootViewController alloc] init];
[appDel.window setRootViewController:rootView];
Related
There is problem in view hierarchy. Here is flow of my app.
When app starts View Controller "A" is Visible. After that storyboard "B" is loaded through "StoryBoard Reference (Push)" ,Where another navigation controller is present and Home screen is loaded. On Click of Menu button in Home screen Side panel is visible.
Now When i click on side panel menu items view Controller "B" is pushed. This View Controller is Pushed Under Home screen and is not Vsible.
Help View contoller is visible under Home controller. I want Help View Controller should come on top of Home controller.
I dont understand what issue is coming..
Any Help will be appreciated..
If you are pushing view controller from side panel shows in image, will never push because it is not in navigation controller.
the answer depends on how you showing your side menu but from assumptions
what you need to do is to set root view controller or keep reference of navigation controller and push from there
If you are presenting side menu then it is not on navigation controller so you should first dismiss this side menu and on the completion of it you should push new view controller (says B or Help). I am writing my code snippet that i am using in my project for demonstration,
- (IBAction)settingClick:(id)sender {
SettingViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"settingsScreen"];
[self dismissViewControllerAnimated:NO completion:^{
[self.vc.navigationController.navigationController pushViewController:svc animated:YES];
}];
}
Above method push setting view controller on current navigation controller after dismissing side menu
Now important thing is self.vc this is the object of previous viewcontroller (Home controller in your case i think) on which side menu was presented.
So my SideMenuViewController has a property like,
#property (nonatomic,strong) UIViewController *vc;
which i am setting with self from previous view controller (in your case from Home view controller) something like,
SideMenuViewController *smvc = [self.storyboard instantiateViewControllerWithIdentifier:#"sideMenu"];
smvc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:smvc animated:NO completion:^{
smvc.vc = self;
}];
And i have used [self.vc.navigationController.navigationController pushViewController:svc animated:YES]; i.e. two navigation controller to push because i have two navigation controller in my view hierarchy to push this new view controller.
You can manage that as per your setup that how many navigation controller you have !!
Hope this will help :)
After digging a lot, I found solution for this.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Home" bundle: nil];
RootController *someViewController = [storyboard instantiateViewControllerWithIdentifier:#"RootController"];
[self.navigationController pushViewController:someViewController animated:YES];
I have set another navigation controller as root ViewController of the window and it worked for me.
Thanks.
Here is the scenario:
The first scene in my storyboard is a login view. It's a UIViewController. When the user is logged in, it shows the home view which is embedded in a navigation controller. I'm adding a log out functionality which should take me back to the first scene in the storyboard which is the login view. How do I do that?
Here is an image of the storyboard showing the login view -> navigation controller -> home view
This is my implementation so far. In the log out action, I clear the session, and pop to root view controller. It does not work because I am still stuck on the home view since it is the root view controller of the navigation controller. However, If I restart the app, the user is logged out and I'm left with the login view.
Code:
[self.navigationController popToRootViewControllerAnimated:NO];
// Set at beginning of storyboard
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
app.loginViewController = [mystoryboard instantiateViewControllerWithIdentifier:#"loginViewController"];
Use unwind segues for that.
In your LoginViewController, declare a method with this signature
- (IBAction)unwindToLoginViewController:(UIStoryboardSegue*)segue
Go to your HomeViewController and control drag from your logout button to the Exit button at the top of your view controller window (see screenshot below), then select the unwindToLoginViewController segue. That's it!
U can pop by using navigationController.viewControllers.Get all View Controllers among navigationController,identify it and then pop.If u have pushed the segue from LoginView to HomeView
if([self.navigationController.viewControllers[0] isKindOfClass:[LoginViewController class]])
{
[self.navigationController popToViewController:self.navigationController.viewControllers[0] animated:YES];
}
Hope it helps you...
Try this answer. First you create a navigation controller. make it "is initial View Controller". After that connect login Viewcontroller as a root view controller And connect home controller with facebook button Action.
Navigation Controller -> Login Controller -> Home Controller
Your Storyboard is look like this
After that when you logout from HomeViewController then Just add this method:
-(IBAction)logOut_Action:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
Its working Fine. Please implement like this and let me know if you face any problem. :)
Try this:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
I'm building a complex app that has kind of a branch in the middle.
At some point in the app, a particular UIViewController is presented, we'll call it mainViewController (shortened mainVC).
The mainVC presents another view controller, by code, using the following code (I strip out parts of it for privacy reasons):
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"SecondaryStoryboard" bundle:secondaryBundle];
SecondViewController *secondVC = [storyboard instantiateInitialViewController];
[self presentViewController:secondVC animated:YES completion:nil];
So the secondVC will later present another view controller, called thirdVC. This is done using a custom segue, set in the storyboard used in the code above, which code looks like this:
#implementation VCCustomPushSegue
- (void)perform {
UIView *sourceView = ((UIViewController *)self.sourceViewController).view;
UIView *destinationView = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width, destinationView.center.y);
[window insertSubview:destinationView aboveSubview:sourceView];
[UIView animateWithDuration:0.4
animations:^{
destinationView.center = CGPointMake(sourceView.center.x, destinationView.center.y);
sourceView.center = CGPointMake(0 - sourceView.center.x, destinationView.center.y);
}
completion:^(BOOL finished){
[self.sourceViewController presentViewController:self.destinationViewController animated:NO completion:nil];
}];
}
#end
As you can see this segue presents the destination view controller modally (by the use of presentViewController:) with a custom animation (a slide from right to left).
So basically up to here everything is fine. I present the secondVC with a classic modal animation (slide up from bottom) and present the thirdVC with my custom transition.
But when I want to dismiss the thirdVC, what I want is to go back directly to the mainVC. So I call the following from the thirdVC :
self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:_animate completion:nil];
That way, I'm calling dismissViewControllerAnimated: directly on mainVC (referenced by self.presentingViewController.presentingViewController), and I'm expecting the thirdVC to be dismissed with an animation, and the secondVC to just disappear without animation.
As Apple says in the UIViewController Class Documentation:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, it automatically forwards the message to the
presenting view controller.
If you present several view controllers in succession, thus building a
stack of presented view controllers, calling this method on a view
controller lower in the stack dismisses its immediate child view
controller and all view controllers above that child on the stack.
When this happens, only the top-most view is dismissed in an animated
fashion; any intermediate view controllers are simply removed from the
stack. The top-most view is dismissed using its modal transition
style, which may differ from the styles used by other view controllers
lower in the stack.
The issue is that it's not what happens. In my scenario, the thirdVC disappears, and shows the secondVC being dismissed with the classic modal slide to bottom animation.
What am I doing wrong ?
Edit :
So #codeFi's answer is probably working in a classic project, but the problem here is that I'm working on a framework. So mainVC would be in a client app, and the secondVC and thirdVC are in my framework, in a separate storyboard. I don't have access to mainVC in any other way than a reference to it in my code, so unwind segues are unfortunately not an option here.
I've been having this exact same issue, and I've managed to visually work around it by adding a snapshot of the screen as a subview to secondVC.view, like so:
if (self.presentedViewController.presentedViewController) {
[self.presentedViewController.view addSubview:[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]];
}
[self dismissViewControllerAnimated:YES completion:nil];
Not pretty, but it seems to be working.
NOTE: if your secondVC has a navigation bar, you will need to hide the navigation bar in between snapshotting the screen and adding the snapshot as a subview to secondVC, as otherwise the snapshot will appear below the navigation bar, thus seemingly displaying a double navigation bar during the dismissal animation. Code:
if (self.presentedViewController.presentedViewController) {
UIView *snapshot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[self.presentedViewController.navigationController setNavigationBarHidden:YES animated:NO];
[self.presentedViewController.view addSubview:snapshot];
}
[self dismissViewControllerAnimated:YES completion:nil];
I had the same issue and I've fixed it by using UnwindSegues.
Basically, all you have to do is add an IBAction Unwind Segue method in the ViewController that you want to segue to and then connect in IB the Exit action to your Unwind Segue method.
Example:
Let's say you have three ViewControllers (VC1, VC2, VC3) and you want to go from VC3 to VC1.
Step 1
Add a method to VC1 like the following:
- (IBAction)unwindToVC1:(UIStoryboardSegue*)sender
{
}
Step 2
Go in Interface Builder to VC3 and select it. Then CTRL-drag from your VC icon to Exit icon and select the method you've just added in VC1.
Step 3
While still in IB and with VC3 selected, select your Unwind Segue and in the Attributes Inspector add a Segue Identifier.
Step 4
Go to VC3 where you need to perform your segue (or dismiss the VC) and add the following:
[self performSegueWithIdentifier:#"VC1Segue" sender:self];
According to the documentation dismissViewControllerAnimated:completion: will allow you to dismiss an entire stack of view controllers by merely dismissing the controller on the bottom of the stack. When I try this, only the top controller is being dismissed.
For example, consider the figure below where controller A and B have both been presented modally. If I dismiss A then both A and B should be dismissed, but only B is being dismissed!
Below is the action that is trigged when the user click on B's button. The root view controller dismisses A (its presented controller), but only B is dismissed!!
- (IBAction)dissmissAandB:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
ViewController *rootViewController =
(ViewController *) appDelegate.window.rootViewController;
[rootViewController.controllerA dismissViewControllerAnimated:YES completion:^{}];Below
}
The root view controller's controllerA property is set when the first segue is prepared (i.e., when A is presented). Any ideas why this is not working as advertised?
Actually, I discovered the correct call is
[rootViewController dismissViewControllerAnimated:YES completion:^{}];
instead of
[rootViewController.controllerA dismissViewControllerAnimated:YES completion:^{}];
The receiver of the method is the controller that presented the bottom controller not the bottom controller itself.
In all view controllers I present a ModalViewController after I dismiss modal view controller I want to call a method in current view controller.
I have to presentModalViewController I cant push it because it is a form sheet. Since I cant push it (void)viewDidAppear:(BOOL)animated is not called when I dismiss the form sheet.
Btw form sheet is a settings menu and I have to call it in every view controller, so I cant use notifications because there are over 20 View controllers and only one settings menu;
Navigation controller -> Root- > VC1 - > VC2 - > VC3 ->VC4........... VC20......
| | | | |
Menu Menu Menu Menu Menu
I present menu:
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
SettingsListViewController *settingsVC = [sb instantiateViewControllerWithIdentifier:#"SettingsListViewController"];
UINavigationController *modalViewNavController= [[UINavigationController alloc] initWithRootViewController:settingsVC];
modalViewNavController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
modalViewNavController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:modalViewNavController animated:YES];
I dismiss it :
//dissmiss view
[self.navigationController dismissModalViewControllerAnimated:YES] ;
In View Controllers I want to Call;
[self.navigationController popToRootViewControllerAnimated:NO];
Is there a way to call a method in View Controller when form sheet is dismissed ?
Since iOS 5, you can use the presentingViewController property of every UIViewController to see 1) if they're being presented modally in the first place and 2) who it is that's presenting them modally then. So if you present your form sheet by calling [self.navigationController presentModalViewController:modalViewNavController animated:YES], the presenting view controller will then be the root navigation controller and you can tell it to pop to root at the same time you dismiss the modal presentation.
By the way, there's also a storyboard property in every view controller which has originated from a storyboard, so you could use that one directly when instantiating new storyboard view controllers by name.
Make your own delegate and set the view controller that presents the view as delegate.. and call from modalVC when it is about to be dismissed.
You can set modalViewController.parentViewController = self; and then work with it from modal view controller like that if you want to send messages before dismiss:
- (void)viewWillDisappear:(BOOL)animated {
[self.parentViewController doSomething];
}