I have a question about iOS Storyboard.
In my storyboard I have my flow:
Navigation Controller;
Login View Controller;
Detail View Controller;
This is the regular flow if there isn't a valid user session.
When I start the app I need to check if there is a valid user session. In this case I need to visualize directly the 3° (Detail View Controller)
I'm not sure but I think that I need to make the check about user session in my Appdetegate. How can I load, in this case, directly the 3° View Controller?
SOLUTION 1
I think what you can do is not check a valid session at all. Here is what I think
1) Create a BOOL called isUserSignedIn that is initially set to NO and save that in you NSUserDefaults. (AppDelegate didFinishLaunchingWithOptions method)
2) Once user makes a valid Login set the BOOL to YES. Save BOOL in NSUserdefaults
3) When user logs out set BOOL to NO
4) Everytime the user opens the app. In AppDelegate's didFinishLaunchingWithOptions check of the BOOL is yes or no. if no loads LoginViewController. if yes do nothing.
This is how I'd do it. Always save every BOOL changes in NSUserdefaults and loads from there.
SOLUTION 2
But if you want to load your DetailViewController in your AppDelegate use this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//check your login session here…
//if session ok loads DetailViewController
DetailViewController* detail = [[DetailViewController alloc] init];
[self.window.rootViewController presentViewController:detail
animated:NO
completion:nil];
//if not
// do something..
return YES;
}
Related
I have coded my app to return to a specific view controller upon relaunch. I want it to pull data from Core Data. It works fine if it the app is just backgrounded. But if the app is terminated and starts cold, it goes to the top level view controller designated in AppDelegate. I've searched StackOverflow and other forums, but have not found anything addressing this particular issue.
The app delegate has opted in. I'm using storyboards. The view controller I want to re-launch to has a restoration ID in the storyboard. The view controller also has implemented encodeRestorableStateWithCoder and decodeRestorableStateWithCoder. I've set it up as Apple recommends, but it's not working on cold start.
I have 3 questions:
How do I force the app to relaunch to this specific view controller (one below the top level) on a cold launch?
Once it relaunches to the target view controller on cold launch, how do I restore the core data? Should the core data be written to disk and restored from there? Or can I just write enough info to disk (i.e., key data) to go pull the data from core data?
Is there a good tutorial for restoring from a cold launch? The ones I've found are only restoring transient data and not showing how to return a non-top level view controller.
Thank you in advance!
Adding applicable code from AppDelegate. If the user was on a profile details screen, I want to return them there even from a cold start. I'm able to confirm that the profile ID is being written to disk and I'm reading it. I do not have "Is Initial View Controller" checked for ProfileVC on the storyboard. But it still returns to ProfileVC no matter what.
NSDictionary *settings = [self readAppState];
if ([settings objectForKey:PROFILE_ID_DICT_NAME]) {
NSLog(#"AppDelegate: didFinishLaunchingWithOptions: PROFILE_ID_DICT_NAME = %#",
[settings objectForKey:PROFILE_ID_DICT_NAME]);
ProfileDetailsVC *controller = (ProfileDetailsVC *) navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
} else {
NSLog(#"AppDelegate: didFinishLaunchingWithOptions: PROFILE_ID_DICT_NAME is null");
ProfileVC *controller = (ProfileVC *)navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
}
You must assign a unique restoration identifier to all view controllers which need to be restored. This includes:
The ones you're really interested in
The root view controller
Any other view controller in between
So if you have a tab bar controller for example, with a tab containing a nav controller and it has a root controller, all 3 must have restoration identifiers for the root controller to be restored.
You can do something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
//do your core data load here
if(your core data condition){
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"someViewController"];
self.window.rootViewController = viewController;
} else {
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"someOtherViewController"];
self.window.rootViewController = viewController;
}
[self.window makeKeyAndVisible];
// Override point for customization after application launch.
return YES;
}
Careful, instantiateViewControllerWithIdentifier refers to the storyboard identifier not the class name.
First of all look at this tutorial
I think you can find something useful here NSCoding
And about switching between view controllers. Can you provide more details so we can help you with better solution. What is the purpose of switching from cold start? Fore example if user logged in or not.
UPDATE
Given that you are having data base to store all profiles, no matter local or remote database. When user opens profile you can save profile's id (id local data base or in user defaults) and then when application have started add check in route view controller
NSString *savedProfile = [[NSUserDefaults standartDefaults] valueForKey:#"savedProfile"];
if (savedProfile) {
//retrieve needed profile
//open view with this profile
}
For anyone that may be interested, here's how I solved this problem.
I'm going from ProfileVC --> ProfileDetailsVC under normal circumstances. However, if a user was on ProfileDetailsVC when the app was killed, I want to return them to it. The data on both screens is populated from core data.
Most of the tutorials I found show how to restore specific fields on a screen that is restored, but I couldn't find one to do exactly what I was trying to do.
Here's the pseudo code:
ProfileVC:
viewWillAppear:
if (savedProfile)
get profile ID from disk
read core data to get profile
set profile in ProfileDetailsVC
performSegueWithIdentifier(ProfileDetailsVC)
ProfileDetailsVC:
willMoveToParentViewController:
remove profile ID from disk
viewDidLoad:
save profile ID to disk
I can post actual code if anyone needs it.
I am developing an iPad application where the user just can (in some cases) open the app, if he types in a password.
Therefore I need something like a LoginViewController. But first, let us handle the common case, where the app just needs to display the HomeViewController:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//...
self.controllerHomeView = [[HomeViewController alloc] initWithNibName:#"HomeView" bundle:nil];
self.controllerHomeView.showInitialGuide = isFirstLaunch;
self.window.rootViewController = controllerHomeView;
[self.window makeKeyAndVisible];
//..
}
But, like I said before, some users may have defined a password and if so, we need to display a login screen. That's what I did to enable such a feature:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
//---
if(isAppPinSecured && !loginIsAlreadyDisplaying) {
LoginBackgroundViewController *controllerLoginBG = [[LoginBackgroundViewController alloc] initWithNibName:#"LoginBackgroundView" bundle:nil];
self.appPinInputBG = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default-Landscape#2x~ipad.png"]];
self.appPinInputBG.frame = CGRectMake(0, 0, 1024, 748);
self.appPinInputBG.userInteractionEnabled = YES;
[controllerLoginBG.view addSubview:self.appPinInputBG];
//present new root = background
self.window.rootViewController = controllerLoginBG;
//...
}
}
What I am doing is, changing the root view controller to LoginViewController and if the user puts in the correct password, changing back from the LoginViewController to the HomeViewController.
Everything works fine so far, except the orientation. If the current interface orientation is unknown, because the iPad e.g. is laying on a table, the LoginViewController orientation is LandscapeRight instead of the one in the HomeViewController (LandscapeLeft).
It works properly if holding the iPad in your hands, but otherwise precisely not.
Any suggestions on how to fix that issue? I did set my app orientation in the plist file (Landscape Left). I do implement shouldAutorotate with UIInterfaceOrientationIsLandscape(...) in both - Home- and LoginViewController.
Thanks in advance!
Argh, the problem was, that I tried to push a view controller modally before viewDidAppear was called -> you should never do this...
After changing my code due to this error it worked like a charm
I think what is happening is that the preferred orientation is set on the initial root controller - when you switch it out, it doesn't get set.
I would suggest you consider always making HomeViewController the root controller. And instead of switching the root controller, push the LoginViewController (with no animation if you want it to show up instantly):
[self.navigationController pushViewController:controllerLoginBG animated:NO]
That way you'll be able to do things like logout and pop to the HomeViewController. It will also keep your view controller navigation consistent.
I have a one time agreement view that will only show up when user launches app first time.
I use storyboard IOS 5.1 and My rootview is a navigationviewcontroller. My agreement view has no connection with navigation controoler I just want to present a modalview pop up then dismiss the view:
I use following code but it doesn't do anything app just continues and launches navigationviewcontroller I put flags, yes app enters the if statement. :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(login==ok){
UIStoryboard *storyboard = [UIApplication sharedApplication].delegate.window.rootViewController.storyboard;
UIViewController *loginController = [storyboard instantiateViewControllerWithIdentifier:#"AgreementViewController"];
[self.window.rootViewController presentModalViewController:loginController animated:YES];
}
return YES;
}
How can I switch a viewcontroller that has no connection to storyboard and dismiss it?
In the app I've been working on last weekend I had a similar situation. I wanted to show a seperate storyboard the first time for a guided setup.
My code looked pretty much the same as yours and it gave me a warning when I was debugging it.
This was because of the fact that the rootViewController I was trying to present my one-time ViewController on, isn't visible at this point in the app.
So, instead of presenting my one-time view controller "on" the rootViewController, I decided to set my one-time view controller as the rootViewController.
In your case it would be something like:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(login == ok) {
UIStoryboard *storyboard = [UIApplication sharedApplication].delegate.window.rootViewController.storyboard;
UIViewController *loginController = [storyboard instantiateViewControllerWithIdentifier:#"AgreementViewController"];
[self.window setRootViewController:loginController];
}
return YES;
}
To dismiss it, you could probably add a segue from your one-time view controller to your first view controller or you could use code similar to the one in your question.
Another approach would be to do the check and view switching in your first "normal" view's viewDidAppear, instead of in the AppDelegate.
Hopefully this will point you in the right direction.
I have a login screen and what I want to do is to detect whether the user is already logged in and eventually popup a modal.
I created a custom segue that shown the modal with no animation.
#implementation NonAnimatedSegue
-(void) perform {
[self.sourceViewController presentModalViewController:self.destinationViewController animated:NO];
}
The thing is that calling this segue from viewDidLoad or viewWillAppear takes no effect. If I call it from viewDidAppear, the login screen flicks for a while before the modal opens.
Any idea how to solve this?
The way I got around this problem was to check in the application finished launching and set the root view controller as needed. Like so:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/*stuff*/
/*get the view controller from storyboard/nib*/
[self.window setRootViewController:CorrectViewController];
}
It wouldn't show a modal view but just show the login on launch.
I have an application that can be used only if the user is authenticated. In particular, I created two different UIViewController. The first is called LoginViewController while the second is called HomeViewController. In applicationDidFinishLaunching: method, LoginViewController is created and then added to rootViewController property like this:
LoginViewController* loginCtr = ... // alloc and initiWithNibName...
self.window.rootViewController = loginCTr;
[loginCtr release];
Whitin LoginViewController I created a method that performs the login. When the user has been authenticated, I perform a method, called performLogin.
- (void)performLogin
{
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate switchView];
}
where swicthView method has been implemented inside the Application delegate class.
- (void)switchView
{
if(VIEW_TYPE == kLogin) // Display Login
{
// create a new LoginViewController and assign it to rootViewController
}
else // Display Home
{
// create a new HomeViewController and assign it to rootViewController
}
}
Given the previous code, is it possible to implement a more elegant mechanism to manage login / logout transition or does this type of implementation could be considered a valuable solution?
Thank you in advance.
Another option, I will set the HomeViewController as the rootViewController. In the viewDidLoad or viewDidAppear method (before I display any information on the HomeViewController), I will check whether the user has login credential. If not, I will display the loginViewController as a modal to request user login credential. In this case, I don't need to change the rootViewController of the window.
Starting from iOS 5 I started to manage login/logout transitions using the new containment API for UIViewControllers.
Implementing a Container View Controller
If iOS 5 is not available I would see a similar approach writing-high-quality-view-controller.
Hope that helps.