selecting alternative first view controller from story board at application startup - iphone

I've just started on iOS programming and so far the tutorials and answers I found here have been a great help to move forward. However, this particular problem has been bumming me all night and I can't find an answer that "feels right".
I'm writing an application that connects to a remote service and the users need to sign in before they can use it. When they start using the application, their first view should be the sign in dialog; when they've authenticated before, they immediately see the overview page.
The project uses story boards - which I think is a great feature - so most of the code that selects and loads the root view controller is already taken care of. I thought the best place to add my logic is the application:didFinishLaunchingWithOptions: method of the AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
// select my root view controller here based on credentials present or not
return YES;
}
But this brought up two questions:
Inside this particular delegate method, the root view controller has already been selected (and loaded?) based on the story board. Could I move to an earlier spot in the loading process to override the first view controller selection or would that needlessly complicate matters?
To override the first view controller I need a reference to the story board, but I couldn't find a better way than to use the storyboardWithName:bundle: constructor of UIStoryboard. That feels wrong, the application should already have a reference to the story board, but how can I access it?
Update
I worked out the second issue I was having, as I found my answer here:
UIStoryboard: What's the Correct Way to Get the Active Storyboard?
NSBundle *bundle = [NSBundle mainBundle];
NSString *sbFile = [bundle objectForInfoDictionaryKey:#"UIMainStoryboardFile"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:sbFile bundle:bundle];
The above will create a new story board instance; to get the active instance, it's a whole lot simpler:
UIStoryboard *sb = [[self.window rootViewController] storyboard];
In the story board file itself you have to set an identifier for the view you wish to load, e.g. LoginDialog. Afterwards you instantiate the view like this:
LoginViewController *login = [sb instantiateViewControllerWithIdentifier:#"LoginDialog"];
[self.window setRootViewController:login];
Within another view controller, the following suffices:
UIStoryboard *sb = self.storyboard;
LoginViewController *login = [sb instantiateViewControllerWithIdentifier:#"LoginDialog"];
[self presentViewController:login animated:NO completion:nil];

You can just reset the root view controller of the window
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
if(your_condition) {
UIViewController *newRoot = [your implementation];
self.window.rootViewController = newRoot;
}
return YES;
}
This is worked for me, Xcode5.0.1

I have a similar scenario as yours. My application uses a UINavigationController as the root view controller. If the user is logged in, I want to present him/her with NotLoggedInViewController, while if it's logged in I want to show the LoggedInViewController.
In a storyboard a UINavigationController can only have one child, so you have to be able to programmatically assign another root view controller to it.
I start by creating a custom navigation controller class, let's name it MyNavigationController. In the storyboard I assign this custom class to the navigation controller object.
Still in the storyboard, I then model both view controllers, and connect one of them to the navigation controller object. Since I need to be able to access them from my code later on, I assign each of them an identifier using the XCode inspector on the right. These identifiers which can be arbitrary strings, but to things simple I just use the class names.
Finally I then implement the viewDidLoad method on MyNavigationController class:
BOOL isLoggedIn = ...;
- (void)viewDidLoad {
id rootController;
if (isLoggedIn) {
rootController = [self.storyboard instantiateViewControllerWithIdentifier:#"LoggedInViewController"];
} else {
rootController = [self.storyboard instantiateViewControllerWithIdentifier:#"NotLoggedInViewController"];
}
self.viewControllers = [NSArray arrayWithObjects:rootController, nil];
}

I had hardly used storyboard & probably this is not the exact answer to your question. But I will suggest you some way what I did in my project created without using a storyboard.
In didFinishLaunchingWithOptions AuthenticationViewController is the first view loaded. It asks login credentials. Once entered it will enter the actual ViewControllers(viz. TabBar &all..) used by project.
Interesting feature added to project is, when you enter credentials I popped up an UIAleretView that asks user to choose one of the three options.
Save Credentials without passcode
Save Credentials with passcode
Dont save credentials
Here pass code is nothing but 4digit number entered by user. Whenever he wants to 'Save Credentials with passcode', I pushViewController that shows NumberPad instad of default keyboard & popviewController when it finishes entering of pin. If user 'Dont save credentials' & later on while playing the app wants to go for other authentication options then I added the last tab of TabBarController as 'Settings' tab inside which I allow user to choose one of the Authentication options, popped as UIAlertView in the beginning of app start after login.
Dont forget to Save credentials in keychain
In a nutshell,
AuthenticationViewController-> check if login credentials are stored in keychain
1.1. If not stored(i.e. 3. Dont save credentials)-> then show Login page.
1.2. If credentials are saved in keychain-> extract them & see if it is tied with passcode.
1.2.1. If it is tied with passcode(i.e. 2. Save Credentials with passcode
)-> then show passcode page.
1.2.2. If it is not tied (1. Save Credentials without passcode)-> then show/load you project's TabBarController hierarchy or other stuff. here actually your app start.

With the main storyboard already loaded, it's just a matter of finding its reference so that I can instantiate another root view controller:
UIStoryboard *mainStoryboard = self.window.rootViewController.storyboard;
self.window.rootViewController = [mainStoryboard
instantiateViewControllerWithIdentifier:#"view-controller-id"];

Related

Change XIB View at app start

What is the right way to change XIB View which loaded at app start depending on some app settings. Of course I know how to get all settings I need.
In your application's delegate, in the method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(yourSettings)
myViewController = [[MyViewController alloc] initWithNibName:#"FirstNibName" bundle:nil];
else
myViewController = [[MyViewController alloc] initWithNibName:#"SecondNibName" bundle:nil];
self.window.rootViewController = self.myViewController;
[self.window makeKeyAndVisible];
return YES;
}
And that's it. Probably you may want to save your settings in your user defaults so you can load the view properly.
However, I would use 2 different view controllers, as you probably want them to do different things, not just modify some graphics.
EDIT-
Let me see if I understand. You will always load the first view controller and, if some conditions are met, you modally want to present the second view controller that will get dismissed at some point, returning the user to the first view controller. If this is the case, I suggest you move the code in your first view controller, in viewDidLoad or better yet in viewDidAppear, as this view controller will always get loaded. Also this way the user can see that he will eventually go to that view controller. I use something like this in applications the user needs to login to so that it will be obvious for him that he cannot continue until he does login.
I can't say that this is the right way to do it, because it's up to the programmer how he arranges his code, but it would seem to me that the place that controls what view and how it appears belongs in a view controller and not in the delegate, especially considering that your first view controller always gets loaded. It should be up to that view controller to see if it presents the second one or not.

How to develop a TabBar based application with a login functionality?

I am developing an application where i need to show a list as a menu(Courses,lessons,grade,logout) to the user. so even before this i need to show a login screen. Only upon successful and valid login i need to re-direct the user to the menu. So i have planned to develop a tabBar based application with 4 tabs. Here i am confused on how to add the login view controller even before the TabBar controller is loaded. I want the first tab to be selected every time. As of now i am adding my TabBar controller as a rootviewcontroller to my AppDelegate window and then presenting the login view controller as a modal view controller. But the problem here is even before the Login View controller is loaded, my courses view controller is loaded because the tabbarcontroller is loaded first. My actual requirement is i need to load the course view controller with the list of courses based on the inputs given in the Login View controller. But loadview of course view controller is loaded even before the load view of login view controller. so my list of courses is always the same irrespective of who logs in. I am confused here on how to move forward...Any suggestion here would be of great help...
So, a very quick example, could be; in your loginViewController you should have some method something like this:
//Call this after the user has done with the login
-(IBAction)remove:(id)sender{
AppDelegate *del=(AppDelegate*)[[UIApplication sharedApplication] delegate];
//Set some data based on the user's input (eg some property shared in the AppDelegate)
//del.dataEnterByTheUser=someData;
[del removeLoginView];
}
Then in your AppDelegate (assuming that now the rootViewController is the loginViewController) you could do like this (you can optimize the transition):
-(void)removeLoginView{
UITabBarController *tabVC=[[UITabBarController alloc] init];
ViewController *v1=[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
//v1.data=self.dataEnterByTheUser;
ViewController *v2=[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
NSArray *arrayVC=[NSArray arrayWithObjects:v1,v2, nil];
[tabVC setViewControllers:arrayVC];
[tabVC setSelectedViewController:0];
CGRect rectVC=self.loginViewController.view.frame;
rectVC.origin.y=self.view.frame.size.height;
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.loginViewController.view.frame=rectVC;
} completion:^(BOOL finished){
[self.loginViewController.view removeFromSuperview];
self.loginViewController=nil;
self.window.rootViewController=tabVC;
}];
}
Also remember to set in each viewControllers's initWithNibName: the self.title to set the title on the tabItem.
No need to fiddle around with the rootViewController...
Just add the following code at the beginning of your viewWillAppear: method of the view controller which would normally appear first (most likely the VC you are presenting in the first tab):
[self.tabBarController presentModalViewController:loginController animated:NO];
Where loginController is obviously the view controller which manages your login screen. If you show it without animation, it will be the first thing visible when your app launches (after the default image disappears). I've used the same method to show a disclaimer page which the user must read before using the app. It's working just fine and made it to the store without problems.
Edit: In this solution, the loginController must dismiss itself once the user has successfully logged in:
[self dismissModalViewControllerAnimated:NO]; //Although you might do this animated, this time
You can just change the array of view controllers in the tab bar controller at runtime. That should be sufficient for your purposes.
I've written a small example. Try to login with the following credentials:
username: john, password: doe
username: pete, password: poe
You will see a different combination of tabs depending on the login used.
The example can be downloaded from my Dropbox: http://dl.dropbox.com/u/6487838/LoginTabExample.zip

Show login view controller before tab bar controller

I am new to iphone development. I am developing an iphone application which contains four tabs. I have implemented it using tab bar controller. But now i need to show a login screen without tabs before tab bar controller. I have tried so many methods but didnt get the one i wanted.
Can anyone pls explain how to do this with a code snippet??
Create a new class LoginViewController. When your application launches then add the view to the window. Now when login is successful then remove it from superview and add the MainController.
Create a subclass of UITabBarController (though it's not advised by apple), but for this purposes it should be ok and do this in viewWillAppear
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
BOOL isLogged in = //do something to determine if you're logged in
if(!loggedIn){
LoginViewController *loginViewController = [[LoginViewController alloc] initWithNibName:#"LoginViewControllerNibHere" bundle:nil];
[self presentModalViewController:loginViewController animated:YES]; //or NO if you don't want it animated
[loginViewController release];
}
}
Or add this to a category for UITabBarController and import it in the app delegate or wherever you're using the UITabBarController
Create a new UIViewController subclass with a nib that represents your login screen (I'll refer to it as SignInViewController).
Open your MainWindow.nib file and add a new UIViewController
Set the new UIViewController's class type to SignInViewController
Set the UIWindow's rootViewController outlet to the new SignInViewController
Now create a new nib file and copy your existing UITabBarController to it (it's best to split nibs rather than having a single-mega nib)
Back in your MainWindow.xib change the existing UITabBarController's attributes to specify the nib name that you just created
Check this Link's source code,
it uses Login Controller as modal view with 4 tabs
http://code.google.com/p/tweetero/source/checkout
Also i tried this way ,
in my first tab view - in viewDidAppear - i'll check Login = YES then
show the LoginController
- [self.tabbarcontroller presentMOdalViewcontroller:LoginView animated:YES];
so every time u click on first tab - if u need to Login put a flag - check it & show Login View
Hope this Helps.
The best way to do this is to create a new LoginViewController as other people have mentioned and then set your rootviewController to tabBarcontroller as soon as you authenticate the user. Here is how you can do this in swift, this is snippet to put as soon you authenticate your user in LoginViewController
let tabBarController = self.storyboard?.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
Where TabBarController is the storyboard id of your tab bar controller. It could be anything whatever name you have given it.

iPhone dev - Navigation-based app with a start-up log-in screen?

I'm making a navigation-based app that I want to start out with a screen that let's the user enter a username/password before continuing on to the normal navigation hierarchy of views. I'm trying to think about the best way to go about doing this. Should I create a new viewController for the log-in screen and start out with that and then use presentModalViewController to go to the RootViewController of the navigation hierarchy? Or would it be better to start out in the RootViewController (a tableViewController) and immediately push the log-in view onto the screen, and then pop it if they enter a correct username/pw combo? I'm also curious as to HOW you would start your application with a different view from the RootViewController, because right now that's always the first one that shows up. Thanks!
If the user suspends/resumes your app or locks/unlocks the phone, are you going to make them login again? If so, then I'd choose your second option, pushing the LoginView above the NavigationView. Otherwise I think starting with the LoginView and replacing it with the NavigationView is fine.
Your ApplicationDelegate class specifies which controller will serve as the root controller for the application.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window.rootViewController = self.navigationController;
...
}
In this example, an instance of UINavigationController is set as the root view controller once the application has completed launching (e.g. all resources are loaded and the transition is made from the splash image).
If you want a different controller to serve as the root of your application, then this is where you'd do it.
To answer your question about the design, it depends on the desired workflow. Based on what you've described, I'd start with a barebones view controller that loads up a login view/controller. Upon authenticating the user, this view controller can switch to the main application view. With a design like this, you can easily switch back to the LoginViewController if you need to re-authenticate the user at any point.
ApplicationViewController (root)
- LoginViewController
- MainViewController (this could be a UINavigationController or whatever you need it to be)
Hope that helps!

Best way to accomplish this scenario with viewControllers - iPhone

I'm writing an app that will allow iPhone users to login to their accounts. Once logged in the user can pull information, change passwords, change other types of data via webservices. I'm new to iPhone programming and I'm a bit confused on my viewController setup to handle this. What I would like to do is the following;
1) if the user is NOT logged in - display a view which takes up the entire screen displaying my login/password text fields etc. ( I will have some conditionals set in place checking to see if the user has u/p saved in a plist)
2) if user is logged in or once the user logs in, remove the current full size view and load the tabBar view. If the user logs out then the main login view will load.
I will need two viewControllers for this particular scenario? I've read a great deal on view controllers. Read all of the Apple documentation but I still get confused since there are so many options/methods to use.
As always I thank you in advance.
T
Just one of many possible options:
Create LoginViewController and UITabBarViewController. Application delegate will check on start-up whether it already has user's credentials and show the appropriate controller
if ([dataModel hasUserCredentials])
[window addSubview:[tabControlle view]];
else
[window addSubview:[loginController view]];
LoginViewController performs log in and notifies app delegate:
- (void) loginComplete
{
// XXX animation?
// XXX view(Will/Did)(Appear/Dosappear) and all the stuff,
[[loginContoller view] removeFromSuperview];
[window addSubview:[tabController view]];
}
Here is how I would do it:
The tab bar controller is your main view controller. You initialize it first (in the main NIB file) and add it to the window in your app delegate.
In application:didFinishLaunchingWithOptions:, you check whether the user is already logged in or not. If not, you immediately present your login screen as a modal view controller. If you do this without animation, the user will not notice that the tab bar controller is already present underneath.
When the user logs in, you dismiss the modal view controller and your tab bar UI becomes visible.
You should look at creating a Navigation-Based application. This will have a UINavigationController built in. You can use that to control your windows.