I am really new to IOS so I apologize if this questions is not worded clearly. I have tried searching around but I have not found exactly what I am looking for.
basically in my AppDelegate applicationDidBecomeActive method, I am making a call to my webservice to make sure that the user is still a valid user, and to pull down some refrehsed data, or kick them back to the login page if they are no longer valid.
The part that I am having trouble with is the second part. How can I load and show and specific ViewController(in this case the loginViewController) when the user is found to be invalid? I want to let the normal viewController flow happen when they are valid, which is is doing fine, but I can not figure out how to launch a specific viewController when I need to from AppDelegate.
Any ideas?
I think I got it! I used this code in the AppDelegate to display the ViewController I needed.
UIViewController *loginController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
UINavigationController *loginNavController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginNavController"];
UIViewController *currentVC = self.window.rootViewController;
_window.rootViewController = loginNavController;
[currentVC presentViewController:loginNavController animated:NO completion:nil];
For simplicity, lets say you have a one view app (not nav controller, not tab bar controller - the solution scales but easier to explain). When you get the appDelegate message that the app launched, then make a UIImageView the root view and show your launch image (user thinks you are still booting up). Try to log in, and do this in some other object (not a view controller). If you succeed, you make your desired view the rootView, and users sees it. If the login fails, then you makea login window the rootView. The key here is to have an object that is driving this and can interact with the appDelegate. You could also add this functionality to the appDelegate itself.
Related
I need to present a specific ViewController (defined in swift) from my app and it just doesn't work. Here is what I am doing:
I have a login screen defined in my storyboard in file Main.storyboard. That storyboard has a UINavigationController that segues into a screen with storyboard ID "LoginVC".
When I run the app for the first time, all is well. But when the user backgrounds the app (presses the home button) I am automatically logging off the user, which just means a variable "loggedin" is set to false in the userDefaults.
When the user presses the app icon to launch/foreground the app, it returns to the screen it was on when it was backgrounded, which is pretty standard behavior for an app but is NOT what I want here.
Inside AppDelegate applicationDidBecomeActive, I want to check the user defaults and see if we are logged in, and if not then I want to show the login screen "LoginVC".
My code is shown below. The code doe not throw any errors, but it never goes to the login screen - it just stays at the same old screen. What am I doing wrong?
Note also that MyLoginVC is defined in swift, so I imported -Swift.h int the AppDelegate.
Again, everything compiles, it looks like it should run, it thinks it is logged out and tries to show the login screen but it just.doesn't.work. Any ideas?
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"applicationDidBecomeActive");
BOOL loggedIn=[self.engine is_logged_in];
if (loggedIn) {
NSLog(#"self.engine says we are logged in");
} else {
NSLog(#"self.engine says we are NOT logged in");
[self showLoginUI];
NSLog(#"Are we at login screen yet?");
}
}
- (void) showLoginUI {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
MyLoginVC *loginVC = [storyboard instantiateViewControllerWithIdentifier:#"LoginVC"];
if (loginVC) {
NSLog(#"loginVC is something");
} else {
NSLog(#"loginVC is nil");
}
//
// Neither of these works!
//
//[self.window.rootViewController.navigationController presentViewController:loginVC animated:YES completion:nil];
//[self.window.rootViewController.navigationController pushViewController:loginVC animated:YES];
}
UPDATE: I can't believe I am the first person who ever needed to show a login VC when the app re-enters the foreground and detects that the user is now logged out. Maybe I can provide a few more details. My storyboard is Main.storyboard, and the leftmost (initial) VC is a plain old UINavigationController. Is this automatically the rootViewController? Is the same rootViewController as self.window.rootViewController in the AppDelegate? Is there ANY way to have the app detect that the app just re-entered the foreground and must now show the login VC because the user was logged out when the app went into the background. Someone must have solved this before.
Through trial and error, patience, and lots of searching I stumbled across the correct answer, or at least a way that works. Here it is:
My storyboard has a UINavigationController at the top, which then connects to a LoginVC (I'm not much of a storyboard guy, and I didn't write the interface code, so I suspect "connects" is the wrong word but you get the idea). The UINavigationController has an inspector IF of #"LoginScreen" while the Login VC has an ID of #"LoginVC". The LoginVC class already properly handles the case of app launch and properly checks to see if the user is already logged in, transitioning to the main app screen if the user is already logged in. The case I needed to handle was when the user minimizes the app (by pressing the home button) and where I have overloaded AppDelegate applicationDidEnterBackground to log the user out.
When the user resumes the app (by pressing the icon), the app will enter the foreground and become active again. In applicationDidBecomeActive, I check userDefaults to see if the user is still logged in. If the user is still logged in, I don't do anything else. If the user is NOT logged in, I call my own AppDelegate function showLoginUI to tell the app to switch to the login interface. Here is that function:
-(void) showLoginUI {
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginScreen"];
}
This one line of code inside that function is the only thing I needed to do, and it worked like a charm. It looks like it sets the window rootViewController to a newly instantiated UINavigationController that is the root VC in the storyboard. Not sure what magic is going on under the hood but it worked for me.
To me, pushing this viewController to the top of the stack seems wrong. I would try to pop the other viewControllers off the stack until I was back at the loginVC. UINavigationController provides a very convenient method for this:
[self.window.rootViewController.navigationController popToViewController:savedLoginVC animated:NO];
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/#//apple_ref/occ/instm/UINavigationController/popToViewController:animated:
When your application come back from background to foreground. Then whatever action you want to perform write that code in
AppDelegate -> applicationWillEnterForeground method
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
I'm working with some pre-existing code and frankly I'm a bit lost.
My goal is to create a view that displays on the bottom half of the screen when the user RETURNS to the app. This view forces the user to re-enter their password as verification. I've been informed AppDelegate's applicationDidBecomeActive is a good place to handle this process. The issue I'm running into is that the top half of the screen needs to show the view the user was previously viewing before the application became inactive. What is the best way to do this?
What I've been trying to do:
I made a view controller called passwordVerificationViewController and made a corresponding XIB. In applicationDidBecomeActive I try:
UIViewController *myCurrentController = (UIViewController *)navigationController.visibleViewController;
verifyPasswordViewController = [[VerifyPasswordViewController alloc]
initWithNibName:#"VerifyPasswordViewController" bundle:nil];
[myCurrentController.view addSubview:verifyPasswordViewController.view];
This doesn't work. The reason it doesn't work is because navigationController.visibleViewController is not returning the viewController that is on the screen, as I intended it to. I've also tried navigationController.topViewController but it gives me the same results.
Also of note, all of the view controllers in the app are opened via presentViewController and are presented by a navigation controller. I have tried using the navigationController.presentedViewController property but this always returns null.
Would the way I'm doing it work if I could figure out how to get the correct reference to the current on screen view? Is there a better way I can accomplish my goal?
EDIT: Solved using Helge Becker's method. Perhaps not the cleanest way, but I set up the notification center like he suggested. Then I wrapped my notificationHandler in an if statement with the condition, (self.isViewLoaded && self.view.window) as a means to see if the view controller is visible on the screen.
Post a notification in the app delegate methods.
Let the viewcontrollers then register for the notification.
That will allow all viewcontroller respnd to any qpp condition change.
Usualy became active and will enter background are suffice.
I've been struggling with this a long time now, so I finally gave up on trying to find the answer and decided to ask it right away.
On my app I have a user log in page. The app has a TabBarViewController that has some NavigationControllers in it's items.
My last effort was to put the login screen embedded on a NavigationBarController and make it the Initial View Controller, as in the picture below.
When the app is launched, if the user is logged in the LogInViewController 'segues' to the TabBarController and everything is fine. When the user logs out in the ProfileViewController, there's a segue in this ViewController 'segueing' to the initial view controller.
In the other hand, if no user is logged in, the LogInViewController presents a view so that the user can insert username and password. If credentials are correct the LogInViewController 'segues' to the TabBarController. The problem is that at this point, even if the app is still working good, i get the following warning:
Warning: Attempt to present TabBarViewController: 0xa19a670 on UINavigationController: 0xa526370 while a presentation is in progress!
So I assume this is not the best way to handle all this LogIn/LogOut process.
My question is, where should I put the LogInScreen in the hierarchy?
If by any chance my layout/hierarchy is correct, how to make the warning go away?
You should make your Home screen as your rootViewController and in once your application starts or become active, you can check if user is logged in or not, if not then present the LoginScreen Modally, it will avoid the mess with other NavigationController or TabBarController
Alternate could be to put all the ViewControllers in a MutbaleArray and set the current Index of TabBarController according to the view you want to show? if you don't want to show the LoginScreen after user Logged in, just remove it from your MutableArray, check my answer here, it might help your cause
I am looking for feedback on a best practice approach when you want to present a LoginViewController as a "ModalViewController" in an app with TabBarController.
The case is this:
- Application supports user sessions with login/logout functionality
- When the app is launched it tries to login automaticly and either display TabBarController directly or the LoginViewController (displayed as a ModalViewController to enable a dismiss animation on login)
- The application has a TabBarController with a number of tabs.
- Each time a ViewController is presented the user's state is checked
- If the user isn't logged in a ModalViewController with login functionality should be presented
- If the user manually logout the same ModalViewController should be presented
I have tried to come up with several different designs to handle this and no one feels completely "correct". I have worked on one project where this was handled using a subclassed UIWindow which replaced the apps default UIWindow. The subclass then listened for different Notifications such as "UserLoggedInSuccessfully", "UserLoggedOut" etc, to handle presentation of the ModalViewController.
Are there any other solutions to this? I would appreciate any input and experiences of this kind of solution.
There are different solutions for this problem , you could try these alternatives but first one is the recommended approach I would say,
You can make your HomeViewController which will be first tab of TabBarController as default to open every time, and make the LoginViewController separately, in your AppDelegate you could check the login session in didFinishLaunchingWithOptions and if the session is not valid then show the LoginViewController as ModalViewController otherwise show the TabBarController with all the tabs included.
Alternative could be to have all the Controllers in The Tab but when you present LoginViewController , after user has signed in, add all the controllers in a MutbaleArray and delete the current object which is LoginViewController. Once user is signed in , save the info in the keychain and this way user will not have to login again and again, this way you can avoid showing the LoginViewController every time, once user tries to log out , then add the LoginViewController in an Array and then add all the TabBarController objects after LoginViewController.
You may to do the following ,
when user open the app you have to show hime the loginviewcontroller as the rootviewcontroller then when login successfully you have to make the tabbarviewcontroller as the rootViewcontroller .
then each time when the user run the app you have to check if he has a valid session you will make a tabar as a root
if the the session expire when you on the tabbar , you have to change the root view controller from your app delegate to sign in
So heres my question. I am a silent stackoverflow skimmer and Im using xcode 4.5 storyboards for an application which starts with a view controller which asks for the username and password. My application checks data via a backend server and then if the login is correct it performs a segue which points to the uinavigationcontroller which forwards the screen to the main menu.... The uinavigationcontroller hasnt been tampered with and pushes the mainmenu automatically.
Login_screen -> UINavigationController -> Main_Menu
My question is how do i pass the NSString username from the login screen to the main menu screen.
I understand how to pass data from one screen to the next when the segue is pointing directly to it via the prepareforsegue method as well as performsegue. But in this case the login_screen segue points to the uinaviationcontroller which points to the main_menu controller
My assumption is that i will need to make a custom class which inherits UINavigationController and implement certain methods but since uinavigationcontroller isnt seen i dont think viewdidload or viewwillappear are the right methods.
Any help will be appreciated
I am a new user so i am not allowed to post images but i will fix that as soon as possible
You can use the topViewController property of the UINavigationController.
Cast the UINavigationController.topViewController object to Main_MenuViewController and set the data normally.
Hope this helps
in whatever case you need some container to persist the current selected/active object. I f for whatever reason you can't extend the UINavigationController (I'd wonder why), you can always have some property in AppDelegate you can assign and in your final Main_Menu controller read this property.
Smth like this:
AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSObject* myThing = app.myObj
if(!myThing) {
app.myObj = ...
}