Hello everybody, and happy new year for all of you :]
A while ago, I saw so many questions on SO enquiring about how to implement a login screen to the user before he gets to the home screen on a UITabBarController based app, same way like Pinterest, Facebook and so many apps behaving this obvious way.
So I am writing this quick tutorial to show you how to do that easily in Xcode5.
As usual, you can download the source code below π
Open up Xcode, choose the ’empty application’ template, it’s more suitable than the ‘tabbed’ template here because I need you to do all from scratch for your better understanding.
Name the project ‘LoginTabbedApp’, set the device target for iPhone and click ‘Next’, save the project and click ‘Create’.
Pretty sweet, now run the app. So far, you get an empty screen. Let’s made two tab bar items with their controllers and add them to your tab bar controller, let’s call them ‘Home’ and ‘Settings’.
Select your project folder in the project navigator, select ‘New File…’, from the left menu, choose ‘Cocoa Touch’, then from the list, choose the ‘Objective-C’ class, click `Next`. On the next screen, type in ‘HomeViewController’ as the class name and make sure it’s a subclass of a ‘UIViewController’ (of course it could be another superclass, but let’s keep it simple).
Make sure the ‘With XIB for user interface’ option is checked then click ‘Next’, and ‘Create’ buttons.
Now repeat the same work you did for the ‘HomeViewController’ to create a second view controller for the settings tab, call it ‘SettingsViewController’. Don’t be lazy, come on π
Here is how the project navigator should look like so far:
Cool, I invite you to add some labels to both ‘HomeViewControllers’ and ‘SettingsViewController’ xib files for reference.
Now, you should implement the ‘UITabBarController’ with the home and settings tabs.
Switch to the ‘AppDelegate.h’ file and add the following property for the tab bar controller:
[sourcecode lang=”objc”]
@property(nonatomic,strong)UITabBarController *tabBarController;
[/sourcecode]
Now select the ‘AppDelegate.m’ file, and change the ‘application:didFinishLaunchingWithOptions:’ method code to the following:
[sourcecode lang=”objc”]
//init the view controllers for the tabs, Home and settings
//Home controller
HomeViewController *homeVC = [[HomeViewController alloc]init];
homeVC.tabBarItem.title = @"Home";
//Settings controller
SettingsViewController *settingsVC = [[SettingsViewController alloc]init];
settingsVC.tabBarItem.title = @"Settings";
//init the UITabBarController
self.tabBarController = [[UITabBarController alloc]init];
self.tabBarController.viewControllers = @[homeVC,settingsVC];
//Add the tab bar controller to the window
[self.window setRootViewController:self.tabBarController];
[/sourcecode]
I commented the code above so I guess it’s self explanatory. You just init two instances for the home and settings view controllers which will get displayed when switching between tabs, then init the ‘UITabBarController’ instance, add the Home and settings controllers to the ‘viewControllers’ array property of the tab bar controller. Finally, and that’s the most important, you add the tab bar controller to the root view controller of your application window (otherwise, it will not be displayed).
Don’t forget to import the ‘HomeViewController’ and ‘SettingsViewController’ in the top of the file:
[sourcecode lang=”objc”]
#import "HomeViewController.h";
#import "SettingsViewController.h";
[/sourcecode]
Run the app, switch between the two tabs to check everything is fine.
Now you are going to add the login view controller which should be displayed to the user before the tab bar controller, right ?
Remember how you made ‘HomeViewController’ and ‘SettingsViewController’ ? I want you to made another view controller called ‘LoginViewController’. quickly, you got 8 seconds to do that.
Ok, here is your new Project Navigator:
Select ‘LoginViewController.xib’ file and add a label and a button, like below:
Now, in order to display the Login screen first to the user, you will need to set the ‘LoginViewController’ object as the root controller of your app window instead of the tab bar controller object. Make sense, hein?
To do so, switch to ‘AppDelegate.m’, import the ‘LoginViewController.h’ at the top of the file, then change the ‘application:didFinishLaunchingWithOptions:’ method to look as below:
[sourcecode lang=”objc”]
//init the view controllers for the tabs, Home and settings
//Home controller
HomeViewController *homeVC = [[HomeViewController alloc]init];
homeVC.tabBarItem.title = @"Home";
//Settings controller
SettingsViewController *settingsVC = [[SettingsViewController alloc]init];
settingsVC.tabBarItem.title = @"Settings";
//init the UITabBarController
self.tabBarController = [[UITabBarController alloc]init];
self.tabBarController.viewControllers = @[homeVC,settingsVC];
//Add the login view controller as the root controller of the app window
LoginViewController *loginVC = [[LoginViewController alloc]init];
[self.window setRootViewController:loginVC];
[/sourcecode]
Now run the app, you should see the Login screen after the app launch.
Notice how you assigned the login controller to be the root controller of the window, that’s the main reason why it’s was shown first after the app launch and not the tab bar controller because now it’s on the top level of your app hierarchy since it’s the ‘root’ of the ‘UIWindow’ object.
Ok, so far so good. But after signing in, you need to get rid of that login screen and show the tab bar controller, because there where your user is supposed to spend most of his time on the app.
So you added a button to the login screen, let’s define an action method for it.
Select ‘LoginViewController.xib’, then switch to the assistant editor on the top menu.
Make sure ‘LoginViewController.h’ is selected for the second editor:
Then hold a ctrl click and drag a line from the xib file to the .h file like below:
In the popup panel, fill in the following informations: name, type, event, etc:
Finally, switch to ‘LoginViewController.m’, import the ‘AppDelegate.h’ file at the top. Locate the ‘- (IBAction)signInAction:(id)sender’ method added automatically by Xcode for you and implement the following code inside:
[sourcecode lang=”objc”]
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.window setRootViewController:appDelegate.tabBarController];
[/sourcecode]
You got it π
The idea is to alter the window’s ‘rootViewController’ property, if the Login screen should be displayed, then put it as the root controller. And if it’s time to show the tab bar controller, then put the tab bar controller as the root.
Run the app, and click the sign in button. Success!
That’s it, you got the idea, now you can implement a sign out button by yourself to redirect your users back to the login screen π
Note: You can also customise the transition of the root view controller by implementing a nice animation, I wrote about that on our forum discussion here.
Here you can download the final project for this tutorial.
Update: As Marcos requested in the comment, here is a Swift version updated for this tutorial π
Please leave a comment below and let me know how you implemented that in your app. Bring your idea here so we can discuss and share together.
Hi, nice work! I was working on implementing the SSO login using facebook api. My idea was to show the FBLoginView before transitioning to the tabBarController. If possible, could you please show me how you could do this as well.
Hi Kenta, Thanx for your comment. Actually it would be a bit different with FBLoginView, I assume you are following this facebook guide https://developers.facebook.com/docs/ios/login-tutorial/#permissions-login
So you need to implement the delegate method `loginViewShowingLoggedInUser:`
This delegate method will be notified whenever the user is logged in, at that moment, you can make the transition to the tabBarController:
FBLoginView *loginView = [[FBLoginView alloc] init];
loginView.delegate = self;//Don’t forget to set the delegate to self in order for the delegate protocol methods to be notified
//implement the following delegate method
– (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
//here perform the transition to tabBarController
}
Let me know π
Malek
Hi Malek, Thanks for this tutorial. I am really struggling to get my Login screen before tab controller. Followed your tutorial, I could get the login screen, but the Tabs doesn’t appear now. I am using storyboard, added everything in AppDelegate & LoginViewController loginBtnClicked event as you have described. My didFinishLaunchingWithOptions code : UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Main_iPhone” bundle:nil];
self.tabBarController = [[MC_MainTabBarController alloc] init];
LoginViewController *loginViewController = [storyboard instantiateViewControllerWithIdentifier:@”loginViewController”];
[self.window setRootViewController:loginViewController];
And loginBtnClicked method contians :
NSLog(@”Showing TabController”);
MC_AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.window setRootViewController:appDelegate.tabBarController];
I get black screen & the messages on execution of above code :
Showing TabController
2014-02-28 12:06:09.536 MintChat[483:70b] Two-stage rotation animation is deprecated. This application should use the smoother single-stage animation.
2014-02-28 12:06:09.537 MintChat[483:70b] Two-stage rotation animation is deprecated. This application should use the smoother single-stage animation.
I am using storyboard, so haven’t implemented the tab code on my own – what these error can be. I am using Xcode 5. Can you please help me with this. From last 3 days I am struggling with this issue and yet am not able to solve it to the best. I would highly appreciate it if you can help me out. Thanks lot.
Hi julie,
Are you sure the tabbarcontroller is a “root” in your storyboard scene ? the error you got usually indicate there is a previous controller insterted before the tabbarcontroller (UINavigationController for instance).
UITabBarController should always be used as a root controller, so in the storyboard, make sure no controller is before it.
Let me know.
I found my solution. I initialized my tabbarController class using Storyboard ID and that did the trick. Thanks.
Oh sorry, I had missed your answer. Anyways, yes tabbarcontroller is the root in my storyboard. My application is a Tabbed template application.
Julie, do you mind sharing your code solution for this? I’m having the same trouble
Thank you
You are welcome π
Thanks for posting this article. I’m a newbie at developing on the ios platform. I used about a couple hours trying figure out how to implement the slide menu with login storyboard preceding. This post help me indirectly figure out how to accomplish that task.
How will one handle LOGOUT ? Need to setup another delegate?
You make my day π
Thanks !!
Hi !
Thanks for this article.
Do you have it using XCODE 6 and Swift?
Thank you again!
Hi Marcos,
I will rewrite the project in swift for you asap π
Thank you very much!!!
Yes, XCODE 6 and Swift !
Here you go π
http://sweettutos.com/wp-content/uploads/2015/02/LoginTabbedAppSwift.zip
Thank you very much!!!!!!!!
Is it possible to go back to the login screen after we’ve changed the root view controller? There isn’t any navigation structure that allows the user to go “back” to the login screen.
Hello π
Moving back to login screen is as simple as moving forward from login screen to tabbar screen. You just need to tell the window that its root is now the login view controller. Here is:
LoginViewController *loginVC = [[LoginViewController alloc]init];
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.window setRootViewController:loginVC];// This will move you to login screen
Hi Deepak, please see my reply on @propstm:disqus comment below π
Great Tutorial Dude π
Thanks Soumya, glad to help π
Thanks. Will this create a new instance of LoginViewController and cause memory leak??
Yes it will create new instance, yet it is safe from memory leaks π
But if you want, you can declare the loginVC as a property in your class scope so that it’s declared once, and use it as self.loginVC
Hi !
I have the same problem , I am using storyBoard on Xcode 6. Please give me a solution for the same. If possible can you please demonstrate it as you have done it earlier.
Thanks in advance.
Hi Karan π
What is the issue you are encountering ?
how can i make the transition more smooth when setting the root view controller. Like with segue based animations
Hi Damir, of course you can. I wrote the solution in the forum section for you and for all fellow readers, please refer here: http://sweettutos.com/forums/topic/switch-the-root-controller-with-an-animation/ π
Hi. Dropbox link is broke, DO you have another one?
Hi Raul, here is a new link to download π http://sweettutos.com/wp-content/uploads/2015/09/LoginViewBeforeTabBar.zip
Hi everyone,
What if we push UITabBarController to navigationController ?
Its working fine for me, Is there any problem ?
Thanks
Hi Muhammad π
Such implementation is not recommended even if it’s working. UITabBarController should be the root of your app window and shouldn’t be a root (or in the stack) of a UINavigationController. Instead, you better try to mimic a tab bar controller, so it would be a UIViewController but with custom views as tabs.
I am having a trouble using your method of setting loginVC as root and then Tabbar as root when the user submit the login details correctly.
Problem is that when the app is run login screen and related screen of login (i.e SignUp/Forgot password etc). are shown first perfectly as you describe but in the backend tab bar is also running. As it is not showing on the screen but it is running.
I have set a timer on one the screen in Tab bar and set a Timertime 10 seconds.
When i run the app it takes near about 10 seconds to submit details and click submit. and when i submit and tabbar is loaded the timer has been terminated.
How to stop tabbar to run parallel with loginVC. I want to run tabbar after the submission of correct login details.
Is there any way???
Hi Syed π
If the tab bar is set as the root of the window then it should not be running in parallel with the login controller. What does the timer do exactly? Please provide your relevant code in our Forum discussion and I will be glad to help you fix this!
http://sweettutos.com/forums/forum/ios-development/
Thanks
I have got my solution by debugging. Tabbar should alloc and init and assigned as root view controller in a special function other than Application didfinishLaunchingWithOption and in the loginVC when your login submission detail are correct instead of setting tabbar as root view controller just invok that function which contain the code related to tab bar allocation, initialisation and assignment as root viewcontroller..
This will tell the tab bar to load when that function is invoked.
While in your case both loginVC and Tab bar are running at the same time because they have been initialised at the same place and at the same time.
Hi Syed,
Sorry, you are wrong about my code. First off, there is absolutely no problem in assigning the tab bar, as the root of the window, INSIDE the didFinishLaunching app delegate method. Second, the tabbar and loginVC are not running at the same time because there is ONE AND ONLY ONE object which is assigned as the root of the window at a time.
Anyway, glad you solved your issue! Keep visiting!