I have a UINavigationController and on it I load a rootView that controlls the login process to my application.
I have recently added some code to my application delegate that checks my settings bundle for a logout request when this logout request happens I would like to either reload the rootView so that it loads the login hud, or just call the method inside rootView that shows the login hud.
This is how I set up the rootView for the navigationController inside my appdelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.navigationController; //Adds RootViewController to the NavigationController interface
// etc
}
What I would like to know is there a way to reload rootViewController? or call a method from it inside the application delegate?
It can be done but it's complicated. Best to avoid it if possible, and the specific requirements will be different for every app. Without seeing the source code for your app we can't tell you how it's done.
The basic process is you need to remove all of them from the view and set all references to nil, and then re-create it either from code or by loading the nib again.
A far better option is to leave the rootViewController where it is, and present a modal login view controller over the top of it. Once the user has logged in, send an NSNotification that the root view controller can observe, and populate its data.
Wait until after the notification has been sent to hide the login controller, and consider having the root view controller block the main thread while it performs any network operations pertaining to logging in. This way the login view (with a "logging in..." message?) will remain visible until the root view is fully populated.
Related
I am trying to change root controller on app delegate.
I am making a server call to check if login session is valid or not. If valid, show welcome page, else show login page.
In the appDelegate , I am making server call in the method didFinishLaunchingWithOptions.
I get response through my datamodel delegate, but by the time I get response from server, the old root controller is already presented on screen.
At present, old root controller is presented first and within a fraction of seconds, new root controller is presented! So it looks like a flickering thing!
Is there a way to hold the old root controller to present untill I get repsonse from server ?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
_dataModel=[DataModal sharedInstance];
_dataModel.delegate=self;
NSString *token=[[NSUserDefaults standardUserDefaults]objectForKey:#"token"];
if (!token) {
token=#"jkfhgjkfg908";
}else{
_dataModel.auth_token=token;
}
[_dataModel checkToken:token];
return YES;
}
//By the time it reaches here, the old root controller is already present on screen
-(void)checkToken:(NSDictionary *)items{
if ([items[#"success"] isEqual:#1]) {
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
UIStoryboard *storyboard=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
app.window.rootViewController=[storyboard instantiateViewControllerWithIdentifier:#"DashboardNavController"];
}else{
//do nothing, defualt is login view
}
}
You should not be waiting on a server call to decide on your root view controller - once didFinishLaunchingWithOptions is called you need to be presenting a view.
Have one Root View Controller present an initial view controller on load and stick to it - it's root for a reason.
If you have a token cached, skip your loginViewController and segue on. Plan for no internet/poor connection/ expired tokens, but don't put asynchronous actions in app delegate that prevent your UI being presented.
Uncheck Is initial View Controller checkbox for a controller in Main.storyboard
Initialize appDelegate's window property: appDelegate.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
Set rootViewController: appDelegate.window.rootViewController = controller;
And call makeKeyAndVisible method: [appDelegate.window makeKeyAndVisible];
Rather than changing rootViewController, using initialViewController as an entrance is a better approach in my opinion. When app is launched there must be a screen to make the user feel everything is going well. Also, you are making a request to server and this process may fail or timed out.
My advice is to make navigation through initialViewController. Make it visible and put some animation or activity indicator and then when the response come, if logged in, go to your apps mainViewController, else go to loginViewController. Also you can check internet connection here and advice user to connect to internet.
You cannot hold the old view controller to present, but instead you can present a view controller with black screen and then after you got the response form the server about the session you could present the view controller based on your needs.
How can I choose which ViewController my App should load when the app goes back to foreground?
Is it by default the last view used in the App?
Is there a way to choose a different view or viewcontroller? If so
how?
Maybe you could use:
Apple Documentation-UIAppDelegate-applicationWillEnterForeground
to change your window rootViewController property :)
So basically, you could instantiate your view controller in this method(from storyboard, or all in code), and do the following:
self.window.rootViewController = yourVc
By default, your last view is the one shown by your application.
If you want to change it to another view (for example a lock screen view), you must change your window.rootViewController inside your ApplicationDelegate code, inside both
- (void)applicationWillResignActive:(UIApplication *)application
and
- (void)applicationDidEnterBackground:(UIApplication *)application
In my app, I am jumping navigation a bit, here is a rough outline of what my navigation does
Login -> Main Screen -> Settings
From settings, I want a logout that goes back to the login page. I do this like this:
UIViewController* requireController = [[[self navigationController] viewControllers] objectAtIndex:0];
[[self navigationController] popToViewController:requireController animated:YES];
This does return me to the login page. However, I would like to release some things in the Main Screen, for example I have a timer that runs a task every 10 seconds, which continues to run (I can see it in the logs). viewDidUnload obviously no longer gets called as of iOS6. viewDidDisapear also is not quite right because that will also get called when going into settings.
What should I do here to get rid of some tasks in my main view controller?
You can use the popToRootViewControllerAnimated: method to directly jump to the root view controller, in your case the Login controller. If you are not retaining the Main Screen or Settings controllers anywhere in your code, then moving to root view controller will invoke the dealloc method on the view controllers getting removed from navigation stack.
You can override the dealloc in your main screen controller and release the resources. Remember, if you are using ARC do not make a call to [super dealloc] as it will give error. But if you are managing memory manually make sure you add the call at the end of dealloc.
Hope that helps!
Set up a protocol on your logout view controller. Before you pop back to the main, call some method to reset the state of that view controller using the protocol (delegate) you created. On the main VC implement that method. AFTER telling the delegate to do whatever, then pop the login VC.
Here are the basics of this pattern.
http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
I would either use NSNotificationCenter or use the - (void)viewDidDisappear:(BOOL)animated method of UIViewController.
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
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.