In my app, if the user isn't logged, it shows a login controller which is embedded in a navigation controller.
When the user is logged, the app should switch to the other navigation controller to display the app.
How can I switch from one navigation controller to another one when the user is logged. ?
Thanks
I'm checking if the user is log in app delegate :
// Check if user is log
let currentUser = PFUser.currentUser()
if currentUser != nil {
// Do stuff with the user
} else {
// Show the signup or login screen
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let nav = mainStoryboardIpad.instantiateViewControllerWithIdentifier("LogInController") as! UINavigationController
self.window?.rootViewController = nav
}
SOLUTION : looks like it works
When user press logIn button :
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let nav = mainStoryboardIpad.instantiateViewControllerWithIdentifier("MainNavController") as! UINavigationController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = nav
Solution 1
One solution would be to use just a single navigation controller. When the user logs in, you would pop all the view controllers used for logging in and push main view controller on the stack.
Solution 2
Alternatively, you could present a new navigation controller modally on top of the login stuff, with main controller as its root. It would be simply presented on top of it.
Solution 3
You can also consider creating the navigation controller with main view controller first and presenting the login navigation controller on top of it. Then, when user logs in you would just dismiss the login navigation controller revealing the main view controller.
Set your navigation controller storyboard ID
let navigationController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SecondNavigationController")
self.presentViewController(navigationController, animated: true, completion: nil)
Hope this helps. :)
Related
Im trying to open a certain view from a push notification but i keep losing the nav bar and the back and next references. this what my storyboard looks like this (with the view i want to open)
this is what i have in my AppDelagate:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController
self.window?.rootViewController = vc
What you're doing is completely replacing the root view controller of your application, this means that all the current UI will be discarded.
What you should do instead is to use your knowledge of your application to direct it to the new content. For example, if your root view controller is a navigation controller, you can cast rootViewController to a nav controller and push it (this will fail if your root view controller is something else, like a tab bar controller).
guard let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController else {
fatalError("Main Storyboard doesn't have a notification controller")
}
guard let nav = self.window?.rootViewController as? UINavigationController else {
return //handle unexpected state
}
nav.push(vc, animated: true)
Another option would be to embed your notification controller into a navigation controller, add a Close button, and present it modally, that way you can present it on top of rootViewController no matter what that controller is.
As we can see in the screenshot your provided, the application's root view controller is the UINavigationController instance.
And according to that, let me offer the next code:
func handleNotification(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController else{
debugPrint("NotificationViewController with identifier 'notification' not found")
return
}
guard let navVC = self.window?.rootViewController as? UINavigationController else{
debugPrint("RootViewController is not an UINavigationController")
return
}
navVC.pushViewController(vc, animated: true) //perhaps your will prefer to use false
}
Beside that, you can use more flexible implementation.
In your AppDelegate post a (NS)Notification when notification intercepted, the relevant view-controller(s) observe the notification, and act when notification broadcasted.
You can also set an identifier to the segue and invoke performSegue method from the observing view-controller
You can set from storyboard -> add view controller -> Embed in navigation controller -> set second view controller -> Attach seque between that controllers. You will see same view controllers like that image .
Is there a way to change root controller when someone is midway into using the app? The way I have it set up right now is that if a new user uses the app they will go into a login page that has the embedded navigation controller. When the user fills out the information they go onto the next page to confirm. The embedded navigation is to go back and change any info. Once they confirm they go to the main page. Now the issue is that the app goes to a different view that already has a Navigation Controller when the user returns and follows some logic. How do I assign the returning user navigation controller when the new user gets into that section? When the user presses back while they are on the blank view they will go back to the confirmation page.
How do I have the confirmation page view get the navigation controller on the top and not the bottom? I'm not sure where to change the Root Navigation Controller but I assume it should be in the prepareForSegue in Confirmation View Controller
if segue.identifier == "showMemoryTable" {
let memoryListVC = segue.destination as? ReturningUserCityDetailTableViewController
memoryListVC?.userData = userData
if userData?.newUser == true {
let newRootViewController = ReturningUserCityTableViewController()
self.navigationController?.viewControllers.first = newRootViewController
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "homeVC")
navigationController?.setViewControllers([viewController], animated: true)
}
I am not sure if I should use setViewControllers but I believe so
If you are aiming to Change the root view controller, you should not do it by by performing a segue, it would be more appropriate to do it like this:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newRootViewController = mainStoryboard.instantiateViewController(withIdentifier: "ReturningUserCityTableViewController") as! ReturningUserCityTableViewController()
let ad = UIApplication.shared.delegate as! AppDelegate
ad.window?.rootViewController = newRootViewController
Note that it changes the whole app root view controller; You could set the desired navigation controller to be the root of the app (obviously you could get them by their storyboard ID).
I need a navigation controller through out my project and my application has a social login initially. Once the authentication is verified user will be pushed to another view, where I display a tabbarcontroller having 2 tabs.
I don't know how to do this in Swift programming. I have embed my viewcontroller in Navigation controller, from here once the authentication is successful how do I push user to tabbar view? Tabbar should also have navigation.
I would like to replicate your idea into what I usually do in the following example.
This is how my storyboard looks like:
As you can see login/signup and Tab bar is not connected with any kind of Segue.
Here Sign in Navigation controller is setup of Initial Controller.
Assign This Navigation Controller an Storyboard ID(e.g.LoginNavigation):
Do the same with Tab Bar Controller, assign Storyboard ID(e.g. HomeTabBar)
Now, you just have to shuffle Root View Controller of the app between Login Nav and Tab Bar.
So if user successfully logs in, changes the application's root view to HomeTabBar using following code:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let home: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("HomeTabBar") as! UITabBarController
appDelegate.window?.rootViewController = home
And when user logs our, again change the root view to Login Nav:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let entryPoint:UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LoginNavigation")
appDelegate.window?.rootViewController = entryPoint
The appDelegate is defined in my constants.swift file :
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
In my app I am making the account page the new root VC when a user logs in.
It looks like this:
Navigation controller -> table view -> menu(modal segue) -> login screen(modal segue) -> account page
When transitioning from login to account I am using:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let vc = storyboard.instantiateViewControllerWithIdentifier("testVc")
let navigationController = self.view.window?.rootViewController as! UINavigationController
navigationController.setViewControllers([vc], animated: true)
This makes the account page the new root VC. But the only problem is that once is shows up both the menu and login form is still visible ontop of the screen.
So how do I clear two old VC's shown as modal?
Update got it to work using:
#IBAction func loginButtonDidTouch(sender: AnyObject) {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("testVc")
let navigationController = self.view.window?.rootViewController as! UINavigationController
self.presentingViewController!.presentingViewController!.dismissViewControllerAnimated(false) { () -> Void in
navigationController.setViewControllers([vc], animated: true)
}
}
But I dont know if this is the right way to do it?
You need to get the reference of those controllers, and then dismissing them.
Try this:
let loginScreen = self.window.rootViewController.presentedViewController
loginScreen.dismissViewControllerAnimated(false) { () -> Void in
let menuScreen = self.window.rootViewController.presentedViewController
menuScreen.dismissViewControllerAnimated(false, completion: nil)
}
When you're calling the original navigation stack and modifying it:
let navigationController = self.view.window?.rootViewController as! UINavigationController
You are setting the new view controller (#testVc) by replacing the only other view controller, "tableview", in that navigation stack.
The modally presented views are not a part of that particular navigation stack and instead are presented above the current navigation stack as new stacks (this gives you a pointer to the new Navigation Controller on top in the form of self.navigationController to push new views)
You can explicitly dismiss the two modally presented views by calling dismissViewControllerAnimated(_:completion:) on each, most likely by propagating the communication through a delegate response or through the completion handler.
New iOS developer here. I am working on a project which requires that the user log-in when they first open the app. From then on, I want the app to open directly to the main flow of the app (a tab bar controller in my case). After doing some research, I have come across what seems to be two main ways to do implement this functionality:
1) Conditionally set the root view controller of the app's window in the app delegate. For example:
if userLoggedIn {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = tabBarController
} else {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let logInViewController: LogInViewController = storyboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = logInViewController
}
2) Use a navigation controller as the app's root view controller, and conditionally set the view controllers managed by the navigation controller in the app delegate. For example:
if userLoggedIn {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.navigationBarHidden = true
navigationController.setViewControllers([tabBarController], animated: true)
} else {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: ViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as! ViewController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.navigationBarHidden = true
navigationController.setViewControllers([tabBarController], animated: true)
}
If I go with the second option, I can easily transition to the app's main flow (let's say a tab bar controller) after I'm done logging in the user. In an appropriate place in the LogInViewController, I can say:
// Transition to tab bar controller
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
self.navigationController?.setViewControllers([tabBarController], animated: true)
Pretty easy.
As for the first method, I am not sure how I would transition to the main content of the app after logging the user in.
What I am looking for here is the "best" way to handle login flow. I've listed two methods but maybe there is another that is even better. Also, if the "best" method is the first I have listed, how can I get to my tab bar controller after I finish logging the user in? Thanks!
Those are both fine options. One thing that I've done that has worked well is implemented a custom container view controller as the root of my app (this might be a subclass of my main view controller like a tab bar controller or navigation controller).
In this container view controller, I can put code to display and dismiss a login view controller based on the user's login status. Note: you can also use temporary full-screen views here to hide your app's main content while the login screen is shown/dismissed - this is an easy way to get very smooth app launch transitions.
I particularly like this method, because the rest of the app doesn't need to worry about the details. Your container can act like a normal tab bar controller or navigation controller, but underneath, you get to encapsulate all of the login UI logic in one place.