The initial view controller on my app is a UITabBarController that displays for logged in users.
For new users, however, my app delegate will point them to a login/registration view controller first:
// New user, show login
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
When the user has completed the login/registration, I then send the user to the tabbar as such:
// Login done, go to main view
[self performSegueWithIdentifier:#"userLoginToMainSeg" sender:self];
However, doing it this way, the LoginViewController is not released (dealloc is not called).
Can someone explain the error in my logic here?
Your modal segue is basically doing:
[loginViewController presentViewController:mainViewController animated:YES completion: ...];
What this means is that mainViewController becomes loginViewControllers presentedViewController:
loginViewController.presentedViewController == mainViewController
mainViewController.presentingViewController == loginViewController
When you're presenting a view controller, the presenting view controller remains in the view controller hierarchy, so that you can later navigate back by calling:
[loginViewController dismissViewControllerAnimated: ...];
So it's perfectly normal that loginViewController is not released, since it's still the window's rootViewController. It's only that loginViewController is obstructed by the presented mainViewController.
If you want to eradicate loginViewController you can set window.rootViewController directly, but that wouldn't animate the transition. You can achieve animation by messing around the view controllers' views, but it's kind of outside the officially sanctioned territory...
IMO the cleanest solution would be to implement a basic container view controller that would be your window's rootViewController, and that could orchestrate the transition between loginViewController and mainViewController by animating their views, and then throwing away loginViewController. It would be kind of a primitive navigation controller without a navigation bar and a navigation stack – just swapping the current view controller with the new one, and throwing away the former.
Related
I am having trouble organizing the registration/login flow of my app. I currently have the storyboard entry point of the app pointing to a navigation controller. In AppDelegate.m if the user is not logged in have:
SignUpViewController *signUpViewController = [[SignUpViewController alloc] init];
self.navController = [[UINavigationController alloc] initWithRootViewController:signUpViewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
This creates the 'sign up' navigation controller.
I have tried to pop this SignUpViewController along with the Navigation Controller to get to the initial screen of the app (the navigation controller initially pointed to in the storyboard), but I have not had any success.
When you use storyboards (and therefore have a initial view controller there) what iOS is doing for you is to set the window.rootViewController to be the view controller with this initial view controller flag.
In your code you are replacing the root view controller for the SignUpViewController embedded in a UINavigationController and that's why you cannot actually pop from it because there is no other view controller previously on the stack.
One solution could be to add a logic on the first view controller that identifies if the user is logged in and in case not you push (or present modally if you will) the SignUpViewController. I hope that helps.
You already set self.navController as the rootViewController of the window. To be able to accomplish what you want you could do one of these:
Programmatically set the navigation controller initially pointed to in the storyboard as the rootViewController. Then present the SignupViewController as a modal.
or
Present the SignupViewController (as a modal) from the Initial view controller in the storyboard instead of in the app delegate
I have a tab view controller with a navigation controller. In the first tab item I click on a button in a view that pops up a view with animated: YES.
Then when that view is done I hit another button that dismisses it. Like:
[self dismissViewControllerAnimated:NO completion:^{
ProfilesViewController *profile = [[ProfilesViewController alloc] init];
[self.navigationController pushViewController:profile animated:YES];
//SHOW YOUR NEW VIEW CONTROLLER HERE!
}];
But everytime this code runs, it dismisses the view, DOES NOT push the profiles controller, and shows the view from the first tab bar item.
How do I push the ProfilesViewController to the screen with a Back arrow?
If you are using dismissViewControllerAnimated to dismiss that means that the VC is presented modally. As such, it doesn't have a navigation controller (so self.navigationController is nil) and thus it can't push anything into the navigation controller.
You should really add a property to the controller which is a delegate or a completion block which can be used to push the controller from another controller (the one that presents it) to dismiss and push the controller.
A second option is to pass the navigation controller, it's a similar amount of code to using a block but not so good.
A crappy option is to use the parentViewController to find the appropriate navigation controller, but that sucks for many reasons.
I have a sign in VC that pushes a sign up VC; both are under a Navigation Controller. From the sign up VC I call modaling a UITabBar VC with the whole application content. Inside the UITabBar VC I have another VC with a Sign Out method. My question is: how is the correct way to go back to Sign In VC? In this scenario, if I dismiss the UITabBar VC I return to the sign up VC.
I have drawn a diagram that describes the scenario:
Any help will be appreciated.
Thanks,
Marcos
You can change the state of the UINavigationController which is presenting the modal view before dismissing it. For instance calling
[(UINavigationController *)self.presentingViewController popToRootViewControllerAnimated:NO]
from the modal view controller, will result in your underlying UINavigationController to get back to its root view controller, which - in your specific scenario - will be the Sign In VC, so when you dismiss the modal view, the underlying view controller will be whatever you desire.
Below is a hierarchy of my navigation controller
MainViewController
|
|
DetailViewController
Then I do the following on DetailViewController
[self presentViewController:reminderController animated:YES completion:nil];
After navigating to ReminderViewController, at some points I do
[self dismissViewControllerAnimated:YES completion:nil];
However, it brings me back to MainViewController instead of DetailViewController
That is weird. Any thoughts about this issue...
EDITED :
The reason I do presentViewController: reminderController animated: completion: on DetailViewController because reminderController is used to send a reminder. Just like goole app or other apps, when sending sth , we are using presentViewController.
Here is a sample of the documentation regarding the UIViewController class:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
Thus, I think you should first use a segue to push your DetailViewController, and then present the reminderController modally, which you'll be later able to dismiss using dismissViewControllerAnimated:completion: without dismissing DetailViewController.
Instead of using dismissViewController:animated: use
[self.navigationController popViewControllerAnimated:YES];
dismissViewController:animated removes all the UIViewControllers in it.
You will save you a lot of trouble if you read the UIViewController and UINavigationController references. Twice ;)
See this post for more details.
i am using a tabbarcontroller in my app and also want to use a loginview.
I have an LoginController which handles the loginprocedure.
I am presenting the loginView modally.
[self.tabbarcontroller presentModalViewController:loginView animated:NO];
And on other point i am dismissing it(after login is successfull):
[self dismissModalViewControllerAnimated:YES];
After dismiss i have just a blank screen. Thats the blank window in my MainWindow.xib, i have checked it with a testlabel.
Throubles with that. No ideas how to solve this issue.
Two things:
The self.tabbarcontroller should not be instantiating the modal view. It should be the initial view controller that the tabbar controller controls. In the initialization of that view controller, you check for a login session and present the login VC.
Make sure you are dismissing that modal VC you created in the method that presented it. You should have a delegate method for your Login VC (something like loginFinished) that is handled in the VC that presented it.