The following scenario:
My iPad app has a SplitViewController as it's main VC. After starting the app (new or from background) I have a fullscreen login view that (obviously) disappears after entering the correct password.
The problem:
After login, I want to present the exact same screen that was there BEFORE moving to background. This works fine UNLESS there is a modal view on top of the split view (like settings etc).
What I tried:
In AppDelegate I store my self.window.rootViewController, make the login vc as my root vc and after login I set my stored root VC as actual root VC. But then the (modal) settings view is not visible and can not be opened again (Warning: Attempt to present VC on SplitVC which is already presenting VC). In fact, no other modal view can ever be opened (unless app is properly closed).
Second try: Instead of setting the login VC as root VC I presented it as a fullscreen modal view on top of my split view. This yielded the same error message as the first try but a different result. After entering background mode the login VC won't be presented at all (since there already was a modal view).
This is b'coz you R trying to present the VC while it is actually loaded as RootViewController .
Try using this :
UISplitViewController :
Once loaded the Root and as well as MasterViewControlller , You will make the UIViewControllers as SubViews for Your
RootViewController.. From the UIViewController , If you want to revert
back to the RootViewController , Try this :
[self.navigationController popToRootViewControllerAnimated:NO];
I just thought your problem is similar.
Hope it helps.
What I did was the following:
Create a property that can store my modally presented VCs (they are all embedded in a UINavigationController)
#property (nonatomic) UINavigationController *navController;
When creating the login vc I store my modal vc (which may be nil which is fine), dismiss it and present the login vc
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginViewController *loginViewController = [storyboard instantiateViewControllerWithIdentifier:#"LoginView"];
self.navController = (UINavigationController *)self.window.rootViewController.presentedViewController;
if (self.navController) {
[self.navController dismissViewControllerAnimated:NO completion:nil];
}
[self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
And when the login is successfull I dismiss the login vc and restore the modal vc (if available)
if (self.navController) {
[self.window.rootViewController presentViewController:self.navController animated:NO completion:nil];
}
Can you try this way.
Root VC is main screen not login page.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Inside the above code, present your login page to your root VC.
- (void)applicationDidBecomeActive:(UIApplication *)application{
//Use below method or similar method to remove any presented VC on Root VC
if ([((UINavigationController *)self.window.rootViewController).visibleViewController isKindOfClass:[RLSplashViewController class]]) {
NSLog(#"AppDelegate dismiss splash page");
[self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
}
//And present your login VC
}
Inside the above code, First and very import, remove any VC that presented on the root VC. And then present login page to your root VC.
I am currently using this way to present splash page(from background or new), in your case, it is login page. Hopes this is helpful for your case : )
Related
I have two UINavigationControllers like this:
LoginNavigationController -> LoginView
HomeNavigationController -> HomeView -> ...
From LoginView I navigate to HomeView modally like this:
[self presentViewController:HomeNavigationController animated:YES completion:nil];
When the app goes to background I use an observer in LoginView that dismisses HomeNavigationController.
There is a case though, in which I must present HomeNavigationController directly, without asking the user to login. I do this from another storyboard and reset my root view controller:
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
UIStoryboard *secondStoryboard = [UIStoryboard storyboardWithName:kStoryMain bundle:nil];
UINavigationController *homeViewController = (UINavigationController *)[secondStoryboard instantiateViewControllerWithIdentifier:kControllerHomeNavigation];
appDelegate.window.rootViewController = homeViewController;
[appDelegate.window makeKeyAndVisible];
But when the app goes to background, I still must redirect to LoginView.
Is there a way to redirect to LoginNavigationController, add the observer and present modally the HomeNavigationController, and all this to happen seamlessly for the users, without them noticing that Login is being created?
If not, should I somehow change my approach to the task?
You can use this approach to get the required results seamlessly,
1) Make HomeNavigationController as the root view controller of your app.
2) In the first view of HomeNavigationController say it HomeViewController, override viewWillAppear and check if you need to show login view ( You may check this by putting any flag to true when user enters to background or something).
3) If you need to show the LoginNavigationController,show it as modal without animation like,
[self presentViewController: LoginNavigationController animated:NO completion:nil];
Please let me know if you find something confusing.
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 a bit lost trying to figure it out...
I have a tab bar based app with login screen at the start. Login screen should be done as Modal View Controller BEFORE tab bar controller appears.
The problem is that I can present it only in viewDidAppear: method of TabBarController. And user can see for half a second content of the UITabBarController. I've tried to move call to viewDidLoad: or viewWillAppear: but it logs an error in console: "whose view is not in the window hierarchy!". As far as I can understand you can only add ModalViewController when all child UIViewControllers of UITabBarController are loaded, ad that happens in viewDidAppear: delegate method.
Do you have any solution how to show login screen without showing TabBarController before?
I've tried 2 ways of displaying ModalViewController, both of them work in viewDidAppear: only
XIB file with login view and using presentViewController: code
self.loginController = [[LoginViewController alloc] init];
[self presentViewController:self.loginController animated:NO completion:nil];
Storyboard, modal segue and calling it from the code:
[self performSegueWithIdentifier:#"loginScreen" sender:self];
Instead of a modal, you might consider pushing the login screen onto a navigation stack. Inside viewWillAppear: you can just instantiate your login viewController and push it. You could also do it in viewDidLoad if you'd like.
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController pushViewController:yourInstantiatedLoginViewController animated:NO];
}
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];
I have an app with a LoginViewController as the initial view.
Note: So in appDelegate.m, self.window.rootViewController is NOT the TabBarController.
After Auth, I present the main part of the app, which has a tabbarController (identifier:tabBar) with two tabs and one tab has a navigation controller. I am using Core Data, so I need to pass MOC.
If I use,
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UITabBarController *obj=[storyboard instantiateViewControllerWithIdentifier:#"tabBar"];
[obj setSelectedIndex:0];// Which tab to show first
[self presentModalViewController:obj animated:YES];
It works good visually. Now I need to pass the MOC. Read about PrepareToSegue method,created a segue (modal, Not shown in pic) from loginVC to my TargetViewController (TabBar>NavigationController1>View1), named the segue "LoginSegue" and used the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
View1 *devicelist = (View1 *)[[navController viewControllers] lastObject];
devicelist.managedObjectContext = managedObjectContext;
}
The TabBar does not show. How do i set the tabbar controller in this case?
I have been trying to get a grasp on getting a reference for the Modal Tabbar, but still not clear. Can some one explain in layman terms how to handle a situation like this?
I think it would be better to use a design that doesn't use a modal transition to the tab bar controller. Modal presentations are generally supposed to be for interruptions to the normal flow of the app, not for getting your main controller on the screen. There are two alternatives, that I think are better. You can leave the login controller as the initial root view controller of the window, but then switch it out for the tab bar controller (which will be the new root view controller of the window, and the login controller will be deallocated). This usually works ok, but I think in this case where you want to pass the MOC from the app delegate (I presume) to a controller in the tab bar controller, I think a second way would be better.
The second way to do this, and the way I usually do login controllers, is to have the tab bar controller be the root view controller of the window, and then present the login controller modally from the viewDidAppear method of the initial view (which would be the one you're calling View1). If you do this presentation with animation set to NO, the login controller will be the first thing the user sees:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static int first = 1;
if (first) {
LoginViewController *login = [self.storyboard instantiateViewControllerWithIdentifier:#"Login"];
[self presentViewController:login animated:NO completion:nil];
first = 0;
}
}
The if statement is in there so the presentation doesn't happen again when you come back from the login controller (you could do something more sophisticated like having a delegate call back to View1 from the login controller indicating that the login was successful if you want, but this works).
If the login succeeds, you just dismiss the login controller, and you'll be there in your first view (if it fails, you just never dismiss it, and maybe put up a message saying the login failed).
If you go this route, then you can pass the MOC in the app delegate like this:
UINavigationController *nav = [(UITabBarController *)self.window.rootViewController viewControllers][0];
View1 *devicelist = (View1 *)nav.viewControllers.lastObject;
devicelist.managedObjectContext = managedObjectContext;