Reputation: 25694
I'm writing an iPhone app that requires a server login. The root view controller for the app checks if the user is logged in and conditionally sends the user to one of two storyboards (login or main app). The root controller is not a UINavigationController (many of the solutions I've seen to this problem assume that it is). If the user gets logged out on the server (timeout, etc), server requests will return an HTTP 401, which I intercept in my web request code... how can I make this bounce the user back to the login screen and/or the root view controller? I've scoured StackOverflow and the rest of the internet for hours and I can't find an answer. I've attempted an Unwind Segue, and it seems to get invoked correctly (no errors), but it doesn't actually unwind to the root view controller, perhaps because I'm invoking it directly on the root view controller, which is the only one visible from my web request class. Any thoughts?
Here's my app flow:
+-------------------+ +--------------------------> | | |RootViewController | | +----------+UIViewController +------+ | | +-------------------+ | | | | | +-------v---------+ +----+----v-----------------+ | |Login storyboard | |SWRevealViewController | | |2 Views | |see note | | +-------+---------+ +---+-------------------+---+ | | | | | | | | +---------------+ +----------v-------+ +----v--------------------------------+ | Drawer | | Main Storyboard | | UIViewController | | Starts with UINavigationController | +------------------+ | | +-------------------------------------+
The SWRevealViewController is something of a strange beast... it displays two UIViewControllers simultaneously, one in front of the other, with the behind one acting like a "drawer", such that the front one can be slid aside to access it.
Upvotes: 0
Views: 1061
Reputation: 25694
As I indicated in a comment on danh's answer, I may have left out an important detail. I am presenting the login screen and main screen as child view controllers of the root view controller. In pondering this for a while, I realized the solution was to swap the child view controllers. Much research and gnashing of teeth followed, and I arrived at a solution which seems to work reliably.
I initially show a screen (here, the main screen) from my root view controller's viewDidLoad
like this:
[self addChildViewController:self.mainViewController];
[self.view addSubview:self.mainViewController.view];
[self.mainViewController didMoveToParentViewController:self];
[self.view layoutIfNeeded];
And then when I want to switch to the login screen, I do this:
- (void)switchToLoginScreen
{
[self switchScreensFrom:self.mainViewController to:self.loginViewController];
}
- (void)switchScreensFrom:(UIViewController *)fromViewController to:(UIViewController *)toViewController
{
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.25
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve
animations:^{}
completion:^(BOOL finished){
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}];
}
The transition the other way works similarly. The switchToLoginScreen
method is invoked from a handler in my app delegate, which has access to the root view controller via self.window.rootViewController
. The app delegate signs itself up as a delegate for the HTTP 401 handler at app start. Thank you danh; while your answer didn't directly solve my problem, it did get me to think about the problem in a new way that led me to the answer!
Upvotes: 0
Reputation: 360
In your login view controller you can add a listener using the local notification center
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDidLogout:) name:@"UserDidLogoutNotificationName" object:nil];
In the method your notification references, dismiss the view controller.
-(void)userDidLogout {
[self dismissViewControllerAnimated:YES completion:nil];
}
Whenever you want to logout just call, and you should get popped to your logout screen
[[NSNotificationCenter defaultCenter]postNotification:@"UserDidLogoutNotificationName"]
Based on your diagram, I wouldn't think about moving back to the logout screen, rather popping all the viewcontrollers until it is revealed
Upvotes: 0
Reputation: 62686
In RootViewController viewDidLoad
, subscribe to a logout notification. In that same class in viewDidAppear
, check the user's logged in state. If user needs logging in, presentViewController
on your LoginViewController, otherwise present the SWRevealController.
In your notification handler, if the user has lost logged-in state, just do this:
[self dismissViewControllerAnimated:YES];
Dismiss, when called on the root vc (self), doesn't care what view controller is on top, or how many there are in between, it removes everything to reveal the vc on which it was called. That root vc will have its viewDidAppear
** fire which you built to do the right thing based on logged-in state.
Upvotes: 2