Login Screen with Storyboarding possible? - ios5

I am playing around with the new iOS 5 features and trying to rewriting one of my apps as pure iOS 5 app using the new storyboarding feature.
To cut a long story short, I have a start screen where the app tries to connect to a server if the user saved some login data, if not, it should ask for them.
Here is how I would do it. I create a Viewcontroller which is doing the connection thing in the viewDidLoad method. If there is no login data or the login is not successful, I need a to do a manual segue to the login screen.
Now is this even possible, or do I need 2 story boards for that ?

I have solved it by putting a login view without any segues (to or from it) like in the screenshot below:
Then, I used a custom class in the tab bar controller to show it whenever I need it.
In the tab bar controller class, I use 'viewDidLoad' to fire up the login view. To show the modal view, I do have a singleton thingy that stores some state, say BOOL isAuthenticated, where I do the magic:
- (void) performLoginIfRequired: (UIViewController *) source {
if (!self.isAuthenticated) {
NSLog(#"Is not authed");
UIStoryboard *storyboard = [UIApplication sharedApplication].delegate.window.rootViewController.storyboard;
UIViewController *loginController = [storyboard instantiateViewControllerWithIdentifier:#"loginScreen"];
[source presentModalViewController:loginController animated:YES];
} else {
NSLog(#"Is authe");
}
}
And, in my case, I wanted it to be shown when the app first starts, but also when it enters foreground again. So, I registered my tab bar controller with the notification center, so I get notified if the app is coming back:
-(void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
In the willEnterForeground method, I do:
-(void) willEnterForeground: (NSNotification *)notification {
[[myStateThingy defaultState] performLoginIfRequired:self];
}

It sounds like you need to use the performSegueWithIdentifier method. Make sure both views are in the same storyboard, link them together using a Push segue, and give that segue a name. Then, from your first view controller's code simply call the performSegueWithIdentifier to perform a manual segue.
Hope this helps!
See also: Conditionally following a segue
Cheers,
Jesse L. Zamora

I had this same issue, and I solved it simply by doing the following: Instead of trying to segue to a login screen(modally or push), I made the login screen my root view controller. In the login view controller's viewWillAppear method, I check if someone's logged in already. If so, I push my home screen:
// mutableFetchResults is an array with my persistent Credentials object
if ([mutableFetchResults count] > 0) { // Someone's already logged in
[self performSegueWithIdentifier:#"Home" sender:self];
}
Also, in the Home screen view controller's viewWillAppear method, I hid the back button with this line, so the user can't go "back" to the login screen:
self.navigationItem.hidesBackButton = YES;
Finally, every page of my app has a "Sign Out" bar button on the top right. Signing out and putting the login screen up was as simple as this:
- (IBAction)signOutButtonPressed:(UIBarButtonItem *)sender {
MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate signOutCurrentUser]; // this method in my app delegate deletes the current Credentials
[self.navigationController popToRootViewControllerAnimated:YES];
}
Hope that was simple enough!

After trying many different methods, I was able to solve this problem with this:
-(void)viewWillAppear:(BOOL)animated {
// Check if user is already logged in
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([[prefs objectForKey:#"log"] intValue] == 1) {
self.view.hidden = YES;
}
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Check if user is already logged in
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([[prefs objectForKey:#"log"] intValue] == 1) {
[self performSegueWithIdentifier:#"homeSeg3" sender:self];
}
}
-(void)viewDidUnload {
self.view.hidden = NO;
}

Related

iOS : is it possible to open previous viewController after crashing and re-launch app?

How to achieve this stuff below? Please give me some guidance for it. I describe my issue below.
When I tap home button and remove app from tray and while I am opening app I get the login screen. I know how to use NSUserDefaults well.
But my issue is that when I navigate 3rd or 4th viewController and I press Home Button and remove app from tray, Then whenever I open app than I want to open with last open viewController.
Also same when my app is Crashing and I am opening it again then I want to open app with last open viewController state.
So I just want to know that is that possible or not? If yes, then please guide me how to achieve this stuff.
Thank you
Yes, both cases are possible.
On crash, you can use UncaughtExceptionHandler to perform some code. In you app delegate, register you handler like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Other didFinishLaunchingWithOptions code
And add your handler method to the same .m file
void uncaughtExceptionHandler(NSException *exception)
{
// App crashed, save last selected tabbar index to the to the NSUserDefaults
[[NSUserDefaults standardUserDefaults] setInteger:tabBarController.selectedIndex forKey:#"LastSelectedTabbarIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
While app runs, to keep track of last selected tab bar controller, use UITabBarControllerDelegate and save newly selected tabbar's index to NSUserDefaults. Short example:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSUInteger tabIndex = [[tabBarController viewControllers] indexOfObject:viewController];
// I have newly selected index, now save it to the NSUserDefaults
}
This code will save last selected tabbar's index to the NSUserDefaults every time tabbar's selected index changes.
Finally, when you app starts (in your didFinishLaunchingWithOptions), read last saved tabbar index from NSUserDefaults and set tabbar's selected index accordingly
self.tabBarController.selectedIndex = lastSelectedIndexFromDefaults;
Edit:
If you also need to restore UINavigationControllers controllers stack, its pretty difficult task. I give you just a quick overview what comes to my mind.
There are 2 cases:
You have custom view controllers initializers and need to pass custom object to those controllers - In this case, its almost impossible (in some reasonable time) implement this
You use only -init or -initWithNibName...: to initialize view controllers in navigation stack. You could enumerate controllers from the root UINavigationController of the tab, get their classes names using NSStringFromClass and save them to NSUserDefaults. On apps start, you would reverse procedure (initialize controllers using their names strings read from NSUserDefaults using something like this: UIViewController *vc = [[NSClassFromString(#"aa") alloc] init];).
I understand you are ok with the code part so i will just give my suggestion
on viewDidLoad of every view controller set a nsuserdefault value of the top most object on navigation array.
if their are not too many branches then you can manage the push at root view controller easily
This is not the proper answer but you can use it for Navigating view after launching.
In AppDelegate file use below codes:---
#import "NewSAppDelegate.h"
#import "NewSViewController.h"
static NewSAppDelegate *globalSelf;
#implementation NewSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[NewSViewController alloc] initWithNibName:#"NewSViewController" bundle:nil];
self.navController=[[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
globalSelf=self;
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
return YES;
}
void uncaughtExceptionHandler(NSException *exception)
{
UIViewController *currentVC = globalSelf.navController.visibleViewController;
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromClass(currentVC.class) forKey:#"lastVC"];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIViewController *currentVC = self.navController.visibleViewController;
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromClass(currentVC.class) forKey:#"lastVC"];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"appDidBecomeActive" object:nil];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
In your login viewController's init method add an observer for notification and in notification method , you can apply if conditions for viewController's name received.and push to that viewController on launching LoginView controller as:---
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(openLastVC)
name:#"appDidBecomeActive"
object:nil];
// Custom initialization
}
return self;
}
-(void)openLastVC
{
NSLog(#"val ==%#",[[NSUserDefaults standardUserDefaults] valueForKey:#"lastVC"]);
if ([[[NSUserDefaults standardUserDefaults] valueForKey:#"lastVC"] isEqualToString:#"GhachakViewController"]) {
GhachakViewController *gvc=[[GhachakViewController alloc] initWithNibName:#"GhachakViewController" bundle:nil];
[self.navigationController pushViewController:gvc animated:NO];
}
}
May this help you....

how to display a alert display only on the application launch first time?

Ha ii everybody i am doing a reader application which has so many functionality in it,pinch gesture for search function,swipe right and left for the previous and next page,tap to hold for the chapter selection view like that,but when the user download the application and use it we have to inform these functionality with a alert-view or a simple pop-up for the application very first launch.I saw it in many reader applications ,i know this is done through NSNotification or something like that,but i dont know how to use this ,please help me to do this.
Thanks in advance.
In case you mean a UIAlertView, thats pretty easy. But if you want a nice looking view notifying the user about different features, maybe add a view controller of which view has all these things and a get started button on it.
Use NSUserDefaults to store if the user has entered the application for the first time as in the link which EI Developer suggested.
In your AppDelegate Class add your changes to this method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],#"firstLaunch",nil]];
//If First Launch
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"firstLaunch"]) {
//Show welcome view
[self.window addSubview:[welcomeScreen view]];
}
else {
[self.window addSubview:[startUpViewController view]];
}
[self.window makeKeyAndVisible];
}
Add another method in your AppDelegate which the welcomeScreen class can call when the user presses the get started button
- (void) getStarted {
[[welcomeScreen view] removeFromSuperview];
[self.window addSubview:[startUpViewController view]];
}
In your welcomeScreen class add an IBAction which calls this method.
- (IBAction) getStartedPressed {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate getStarted];
//set firstLaunch to NO
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"firstLaunch"];
}
Dont forget to add the AppDelegate #import header in your welcomeScreen class
You can probably use the code found in this question:
iPhone: How do I detect when an app is launched for the first time?
Hope it helps! :D

Iphone Back button help

Hi guys i am trying an app this is my link http://web.missouri.edu/~dnk6y2/Shealth%20app/
1.when i click on a annotation it will go inti an another view when i click show bulding it pop back the same view with single annotation and the code iam using for this is
-(void)viewWillAppear:(BOOL)animated
{
if ((i%2==0)) {
[mapView addAnnotations:djObjects];
i++;
}
else
{
if((show1==YES))
[mapView addAnnotation:annotation];
else
[mapView addAnnotations:djObjects];
// i++;
}
}
the app is working fine when i press back button even it is showing one annotation where i need all the annpotaions to come down....
so my question is can i set an action to an back button i have gone thru the apple documentation but i have no clue...
I assume that you're using a UINavigationController. UINavigationBarDelegate is the delegate class and it implements -navigationBar:shouldPopItem. You can put the action you want to trigger in that method.
Alternately, you can put it in -viewWillDisappear after checking that the view is disappearing because it was popped from the stack:
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// action code here
}
[super viewWillDisappear:animated];
}

How do I update the screen before applicationDidBecomeActive?

I need to hide something on the screen when the user has activates the application by switching it to the foreground.
I have tried inserting my code within applicationDidBecomeActive or applicationWillEnterForeground and although it runs OK the old screen with the text I want to hide is displayed momentarily.
How can I hide the field before the screen is redrawn?
Thanks
iphaaw
I think the problem is, iOS will capture a screenshot from your app in the moment it goes to the background, so the animation will work in an instant.
The only way in my opinion to do this is to hide / cover your view in moment the app goes to the background.
Write some code in applicationWillResignActive: to 'hide' whatever you need to hide.
I faced a similar situation but, instead of hiding, I wanted to show a block code screen to grant access. Anyway I think that the solution also applies to your needs.
I often implement a custom base view controller in my iOS applications. So instead of dealing with applicationDidBecomeActive: or applicationWillResignActive: I setup this view controller to listen for the equivalent notifications:
#interface BaseViewController : UIViewController
- (void)prepareForGrantingAccessWithNotification:(NSNotification *)notification;
- (void)grantAccessWithNotification:(NSNotification *)notification;
#end
#implementation BaseViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self addNotificationHandler:#selector(grantAccessWithNotification:)
forNotification:UIApplicationDidBecomeActiveNotification];
[self addNotificationHandler:#selector(prepareForGrantingAccessWithNotification:)
forNotification:UIApplicationWillResignActiveNotification];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)prepareForGrantingAccessWithNotification:(NSNotification *)notification {
// Hide your views here
myCustomView.alpha = 0;
// Or in my case, hide everything on the screen
self.view.alpha = 0;
self.navigationController.navigationBar.alpha = 0;
}
- (void)grantAccessWithNotification:(NSNotification *)notification {
// This is only necessary in my case
[self presentBlockCodeScreen];
self.view.alpha = 1;
self.navigationController.navigationBar.alpha = 1;
...
}
#end

How do i check whether my current navigationController.view = a classes.view? Reason = push notifications. + iphone

so basically in my app delegate i have a navigation.controller
This navigation controller has a view of a class named MainScreen.
In MainScreen.m , i have a IBAction which will bring me to a SelectionScreen.m page by pushing it. here is the coding for it
SelectionScreen *aSelectionScreenViewController = [[SelectionScreen alloc]initWithNibName:#"SelectionScreen" bundle:nil];
[self.navigationController pushViewController:aSelectionScreenViewController animated:YES];
[aSelectionScreenViewController release];
So how do i check if my current navigationController.view = this selectionscreen.view?
The reason for checking which current view it is, is because when i receieve a push notification, i would want to automatically switch to this SelectionScreen.m page and invoke some methods within it. But this checking can only be done in the appDelegate because the didReceiveRemoteNotification method is located in there.
This is how i'm doing it
for example if you have three ViewControllers ,and any of those have possibility to be pushed by NavigationController:
ViewControllerA
ViewControllerB
ViewControllerC
Then what you need to do is:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ([[self.navigationController topViewController] isKindOfClass:[ViewControllerA class]]) {
//do sth
}
if ([[self.navigationController topViewController] isKindOfClass:[ViewControllerB class]]) {
//do sth
}
if ([[self.navigationController topViewController] isKindOfClass:[ViewControllerC class]]) {
//do sth
}
}//end of code
One way is to save selectionScreenViewController as a property of your app delegate, then:
if (self.navigationController.topViewController == self.selectionScreenViewController) {
//...
}
else {
//...
}
Hey guys, i did it in a simple way. In every view controller i had, i removed all objects and assigned an object to an array in the appdelegate. So this way, everytime i go to a new view, the value is different.
So in appdidrecieveremotenotification, i can check that array and decide on what to do accordingly.
Its just a simple way of checking.