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

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!

Related

selecting alternative first view controller from story board at application startup

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"];

How do I manage a login view in a navigation-based app?

I am designing an iphone application in objective C.
As of now, I have a UINavigationController in the beginning of my application
and rest of the navigation was being handled from it.
Now I want to insert a login screen when the application is loaded.
How do I make it independent of my rest of the application ?
i.e. As of now, I created a LoginViewController and added it to the NavigationViewController
of my app. When the user successfully logs in, the application continues with the next screen being pushed into the Navigation Controller.
But the problem with this approach is that, I can still go back to the initial login screen from the navigation item.
I have tried to hide the navigation bar from the first screen after login, but it removes the navigation bar from each of the subsequent screens.
The only working solution I can think of is that, I should manually hide the navigation bar in the start screen and make it visible in the subsequent screens.
Is there any other sane approach ?
I'd probably do your nav controller disregarding the login screen, and then present the login screen using presentModalViewController:animated:
present your LoginViewController in viewDidLoad for the the navigationController once the user login you dismiss it and continue your screen's flow as you want. also you can store that user login your app in the NSUserDefault so you can check this value and present the loginView in case he didn't login otherwise you show the navigation screen as normal
If the user have not login in the app show LoginViewController and don't allow the user to go to navigation controller till then. Once login don't show LoginViewController directly show navigation controller so that user dont have to sign again and again.. To store user login information use NSUserDefaults. Retrieve the info at loading of the app and display controllers accordingly.
Cheers
You need to store in UserDefaults some value - auto login / did login and when app is start you check this value if user isn`t in system u need to create login view controller and present it modaly.
Before your first view appear u need something like this -
BOOL didLogin = [[NSUserDefaults standardUserDefaults]boolForKey:#"isLogin"];
if (!didLogin) {
LoginViewController *loginVC = [[LoginViewController alloc]init];
[self presentModalViewController:loginVC animated:NO];
}
and when user is login you need to save value in user defaults
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isLogin"];
if you vant auto login feature you must store password in keychain
I would suggest to hide back button at your firstviewcontroller and unhide in rest all viewcontrollers.
-(void)viewWillAppear:(BOOL)animated {
self.navigationItem.hidesBackButton=YES;
}
Otherwise another solution is that present the loginviewcontroller in the viewDidLoad method of your firstviewcontroller and as login completes dismiss the presented logincontroller on login button.
Even its the standard method to implement, all apps having login facility would have this type of flow.
Hope this will help you out.

Returning to main view after phone call in iPhone

I know that I should use this when I get an interruption in the app and it comes back:
- (void)applicationDidBecomeActive:(UIApplication *)application
But how would I, when the app becomes active again, dismiss any previously opened view controllers and present the main view controller?
This may depend on your architecture. If you use an UINavigationController to manage your views, you could call popToRootViewController on it to push everything else off the navigation stack and return to the root view (you'd have to maintain a property or something similar in your application delegate). If you have any modal views open, you could maintain a property in your application delegate that contains the currently active modal dialogue (if any) which would be dismissed.
Note though that applicationDidBecomeActive: is also called when the application was put into the background (e.g. because the user went for another app in the meantime) and the user returns to your app (as well as on first launch), so returning to the root view may be something different than the user expects.

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.

Getting iPhone app to display one of two different views

I have a program where we are using a navigation controller and need the app to launch to one of two different views. Basically if certain info has previously been entered then we need the app to launch to view A, but if the info has never been entered then we need it to launch to view B. I am having difficulty getting this to work and am wondering what ways if any I could implement this. I am certain it needs to be done in the app delegate but I am not sure how. Thanks in advance!
Implement the following method in your app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[window makeKeyAndVisible];
if(condition) {
[window addSubview:[mainViewControllerA view]];
} else {
[window addSubview:[mainViewControllerB view]];
}
}
There you can choose which view to load depending on your condition.
From your question, it sounds as though your UINavigationController is defined inside a XIB along with your root view controller. In that case, you will need to load the appropriate view from within application:didFinishLaunchingWithOptions: of your App Delegate. From there, you can set the first view controller for the UINavigationController using setViewControllers:animated:.
I have met this issue and solved it.
In you navigation controller build a container view.
Then depending on your conditions you decided what view to put in the container. You might have built these two views beforehand. Then, you can add the view into the container view.
I think the "Elements" sample has an example of the container view.