UIViewController did not deallocate itself - ios

I'm working on an app that changes it's rootViewController depending on it's state. To make a switch I use this code:
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self createManagedDocumentAndContext];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *storyboardId = [userDefaults boolForKey:#"Profile Created"] ? #"User Stats" : #"Profile";
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardId];
return YES;
}
To switch back I call this method from presented ProfileVC:
- (void)returnOldRootViewController
{
UIWindow *currentWindow = [UIApplication sharedApplication].keyWindow;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
OLDUserStatsVC *userStatsVC = [storyboard instantiateViewControllerWithIdentifier:#"User Stats"];
userStatsVC.userProfile = self.userProfile;
[currentWindow setRootViewController:userStatsVC];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:YES forKey:#"Profile Created"];
}
THE PROBLEM: rootViewController is changed, but previous one is not deallocated. It stays on the "background" of the app - I can see it when VC changes to another one.
The question is how to release it properly?
Thank you very much!

The real problem here is that you are changing the root view controller of the window. Don't do that. You should be setting this once and never again. There should be just one root view controller for the lifetime of the app.
Find another architecture for displaying the correct view controller and switching between them as desired. For example, they might both be children of the root view controller, or one might be a presented view controller in front of the other.

Related

How to call a view controller from AppDelegate i.e. didFinishLaunchingWithOptions when using TabBarController

I am new to iOS programming and do not know much about calling ViewControllers programatically. I am making an app for notes and I want to make it password protected. I used TabBarController and one of the tabs is for Settings (for enabling and disabling password protection). When app starts, I want to know that, is password protection enabled or disabled. And if it is enabled, I want to display a login screen (i.e. calling a different view controller). I think that I should do this from didFinishLaunchingWithOptions method in AppDelegate, but I dont know how to. I tried some of the ways, but failed. Please help me out.
Thanks in advance.
Sorry about the down votes - I don't think you deserve them. I had the same problem when I started iOS. This might help
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//set NSDefaults when you set if password protection is needed - here you are checking to see if password enabling exists
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL passwordExists;
passwordExists = [defaults boolForKey:#"hasCompleted"];
//jump to different view controller depending if BOOL is false or true
//set names (ShowLogInScreenController) In StoryBoardID in the Interface Builder
UIStoryboard *storyBoard =[ UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController *vc = [storyBoard instantiateViewControllerWithIdentifier:passwordExists ? #"ShowLogInScreenController" : #"PasswordProtectionDisabledViewController"];
UINavigationController *questionsNavigationController = [[UINavigationController alloc] initWithRootViewController:vc];
questionsNavigationController.navigationBar.hidden = YES;
self.window.rootViewController = questionsNavigationController;
return YES;
}

Implement a login view controller

i'm developing an app in Objective-C with Xcode 7 for iOS 9 compatible devices, and I'm finding some difficulty in the login section. How should my storyboard be to show the login view controller as root view if the user is not already logged in?
In this app, the root view controller is a simple view controller with a segue to a navigation controller that holds two simple view controllers.
The way that I handle login is to have the login view controller embedded in a navigation controller. It is the initial view controller shown when the app runs.
I set a BOOL variable in NSUserDefaults called LoggedIn and check it every time the app is opened in viewDidLoad of the login view controller. If the variable = NO show the login view controller, and if the variable = YES push to the next view controller (animated:NO) so the first thing the user sees is the screen you want them on.
In your login view controller viewDidLoad:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults valueForKey:#"LoggedIn"] isEqualToString:#"YES"]) {
ViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"viewController"];
[self presentViewController:vc animated:NO completion:nil];
}
Set a Bool to NSUserdefaults after a login was successful. Put this Code in your method which is called after a successful login:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
defaults setBool:YES forKey:#"login.state"];
[defaults synchronize];
// go to your next ViewController
Then check in your Appdelegate.m in the didFinishLaunchWithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([defaults boolForKey:#"login.state"] == YES) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SWRevealViewController *swrvc = [storyboard instantiateViewControllerWithIdentifier:#"revealVC"];
self.window.rootViewController = swrvc;
[self.window.rootViewController.navigationController popViewControllerAnimated:YES];
}
else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
LoginViewController *lvc = [storyboard instantiateViewControllerWithIdentifier:#"loginVC"];
UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:lvc];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
}
}
If you need any help please ask!

using storyboardID to detect first launch

Using NSUserDefaults, I'm able to present one view if it is the first launch and then another view if it isn't. My problem is, no matter what, the program thinks its the first launch. My code is as follows (in my AppDelegate.m file):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen ]bounds]];
NSString *storyboardID;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
bool hasLaunched = [defaults boolForKey:#"hasLaunched"];
if (!hasLaunched) {
storyboardID = #"firstLaunch";
hasLaunched = YES;
[defaults synchronize];
}
else {
storyboardID = #"notFirstLaunch";
}
UIStoryboard *main = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *viewController = [main instantiateViewControllerWithIdentifier:storyboardID];
UINavigationController * navControl = [[UINavigationController alloc]initWithRootViewController:viewController];
self.window.rootViewController = navControl;
[self.window makeKeyAndVisible];
return YES;
}
I'm creating a nav controller programmatically. Another strange thing is that the first launch view controller (which always shows up) has a transparent nav bar over it. However, when I push to a different view controller (by presenting it), the second view controller doesn't have a nav bar over the top. I'm not sure why this would be the case, considering I'm moving the nav controller over to a new view controller.
You are setting the local variable hasLaunched to YES, but you are not storing this variable in NSUserDefaults -
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
bool hasLaunched = [defaults boolForKey:#"hasLaunched"];
if (!hasLaunched) {
storyboardID = #"firstLaunch";
[defaults setBool:YES forKey:#"hasLaunched"];
}
else {
storyboardID = #"notFirstLaunch";
}
I don't really understand your second question. I suggest you ask another question and include screen shots of your issue.

IOS & objective C development - how to make the login page appear to the user?

I am in my first days of IOS app development, I am trying to build an authentication system for an already existent iOS App using Objective C.
The app's rootviewcontroller is a tabsview followed by navigationControllers.
What i've done so far:
1- creating the loginviewController class & designing it's UI in the storyboard
2- the same thing for the "registration" & "recover my password" classes
3- linking the root viewcontroller with the login page with a segue of type modal.
4- linking the login page with the registration & recover my password pages with segues of type push.
Now i don't know the steps that i should follow to make the login page appear to the user when he first enters the app & eventually store his state so he can access the app later without having to enter his credentials every time (unless he logs out).
Any help is greatly appreciated, thank you
I am available for any clarifications or eventually some screenshots/source code if needed.
Edit 1 : this is the content of my didFinishLaunchingWithOptions method in my appdelegate.m :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
TabsViewController *controller = (TabsViewController *)self.window.rootViewController;
controller.managedObjectContext = self.managedObjectContext;
_observer = [[MyStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:_observer];
//Create sub directories in doesn'n exist
NSString *documentsDirectory =[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *pathToFile = [documentsDirectory stringByAppendingString:#"export"];
BOOL isDir = YES;
BOOL isFile = [[NSFileManager defaultManager] fileExistsAtPath:pathToFile isDirectory:&isDir];
if(isFile)
{
//it is a file, process it here how ever you like, check isDir to see if its a directory
}
else
{
[self createSubDirectories];
//not a file, this is an error, handle it!
}
return YES;
}
What should I add/change ??
Set your rootViewController programmatically by checking login status.
You can save your login status as a bool in NSUserDefault after Login & Logout.
After Login
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"LoginStatus"];
After Logout
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"LoginStatus"];
Modify your appDelegate - didFinishLaunchingWithOptions: delegate method as follows
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARD_NAME bundle:nil];
//Check Login status
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"LoginStatus"])
{
// user not logged in
LOGIN_VIEWCONTROLLER *rootviewcontroller=[storyboard instantiateViewControllerWithIdentifier:LOGIN_VIEWCONTROLLER_IDENTIFIER];
}
else
{
// user already logged in
TABBARCONTROLLER *rootviewcontroller=[storyboard instantiateViewControllerWithIdentifier:TABBARCONTROLLER_IDENTIFIER];
}
self.window.rootViewController = rootviewcontroller;
[self.window makeKeyAndVisible];
return YES;
}
Two Options:
1) You will need database (core data) to keep track of users log. It also may help you for offline login. You can create and set property 'isLoggedIn' to find last logged in user.
2) Create and save logged-in user details in file. Refer this file for next start up. If user is logged-out , then delete that file.
Update 1:
Create login View, push tabBar on succesful login , followed by navigation controller for further flow. Refer image :
Fixed it,
I actually changed the rootviewcontroller by adding this line of code in appdelegate
LoginViewController *controller = (LoginViewController *)self.window.rootViewController;
I also changed the entry of the application in my storyboard to be loginviewcontroller.
And I also added a method called prepareforSegue in my loginviewcontroller to pass as a parameter the managed objectcontext because it caused some errors in the beginning:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toHome"]) {
TabsViewController_iPhone *tabsViewController;
tabsViewController=[segue destinationViewController];
tabsViewController.managedObjectContext=self.managedObjectContext;
}
}
Hope to have helped someone out there. If you need more explanations on how I fixed the problem I am available. Thank's for everyone.
Edit:
After taking some time to understand how objective C works, I came to the conclusion that my answer above was not complete, so this is actually what happend:
After the user logs in the first time, I store his user & password in NSUserDefaults.
When he logs out I set the NSUserDefaults user/pass variables to Null.
So now I added a test to the AppdDelegate' didFinishLaunchingWithOptions method that says:
if the user/pass variables on NSUserDefaults are set -> go to the main menu screen of the app
else -> go to the login screen of the app.
this is the code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults stringForKey:#"username"];
NSString *password = [defaults stringForKey:#"password"];
if (username != nil && password != nil)
{
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
// determine the initial view controller here and instantiate it with
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HEQMainMenuView"];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
}
else
{
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
// determine the initial view controller here and instantiate it with
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HEQLoginView"];
self.window.rootViewController = viewController;
//viewController.man
[self.window makeKeyAndVisible];
}
}
Hope this helps :D

Change Initial View Controller for First Launch

I know this question has been asked but the methods are not working for me. I have a view controller that contains a tutorial, which I only want to display as the initial view the first time the app is opened. After that, the main menu will always be the initial view. The storyboard ID for my Tutorial screen is "Tutorial". I have the following code to detect first launch, but I can't figure out how to make the tutorial appear:
- (void)viewDidLoad
{
if ([[NSUserDefaults standardUserDefaults]boolForKey: #"FirstLaunch"])
{}
else{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can change initial controller in two ways:
Check 'Is Initial VC' checkbox at Interface Builder or
Configure it at -application:didFinishLaunchingWithOptions: method
Changing initial view controller using Application Delegate
In -application:didFinishLaunchingWithOptions: in your application delegate (AppDelegate.m) add if/else to check the necessity of tutorial:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if (![[NSUserDefaults standardUserDefaults] boolForKey: #"FirstLaunch"])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
/*** load vc ***/
}
return YES;
}
To set the initial controller you have to initialize window property, create vc and set it as root:
// 1. Initialize window
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
// 2. Get storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
// 3. Create vc
TutorialViewController *tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([TutorialViewController class])];
// 4. Set as root
self.window.rootViewController = tutorialViewController;
// 5. Call to show views
[self.window makeKeyAndVisible];
Hope it helps!

Resources