I have a LoginView, where after user authentication the user is sent to a corresponding homeView. The transition is made programmatically without segue. Then, depending on the user type (lets say, superuser and tester), the user can browse from his homeView. Both homeViews are preceded by a NavigationController.
NavigationController -Segue-> SuperView -Segue-> SecondView
->LoginView |
NavigationController -Segue-> HomeView <-Segue -
|
-Segue-> View4 -Segue-> View5 ...
Here is the storyboard flow
And the problem: in the simulator, after the login, the first view looks great, but all the next views don't have a navigation bar. In the storyboard, however, everything looks fine.
The transition is made as follows:
func transitionToSuperHome() {
let supervisorViewController = storyboard?.instantiateViewController(identifier: "superVC") as? SuperuserViewController
view.window?.rootViewController = superuserViewController
view.window?.makeKeyAndVisible()
}
and
func transitionToHome() {
let homeViewController = storyboard?.instantiateViewController(identifier: "homeVC") as? HomeViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
This is how the second (and all subsequent) view looks like
any idea what can cause the problem?
It looks to me like you are setting the root view controller in a detached state. A simple fix might be to embed your root view controller inside of a navigation controller in order to avoid the detached state. This is also why your screenshots appear to be presented modally instead of in a navigation stack.
window?.rootViewController = UINavigationController(rootViewController: homeViewController)
Related
So I have an if statement that has 2 outcomes, rootViewController becomes loginViewController or mainSearchViewController. The storyboard for mainSearchViewController has an embedded TabViewController and a NavigationViewController. However, by setting the root view controller, I lose access to both the TabViewController and a NavigationViewControllerand its only the mainSearchViewController that is active. Here is my code:
func setRootViewController() {
if Auth.auth().currentUser != nil {
let mainSearchViewController = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.mainSearchViewController)
view.window?.rootViewController = mainSearchViewController
view.window?.makeKeyAndVisible()
} else {
let loginSearchViewController = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.loginViewController)
view.window?.rootViewController = loginSearchViewController
view.window?.makeKeyAndVisible()
Lose access to Tab Bar Controller and Nav Controller when making the root view controller the view on the very right
Edit: I have tried to set the rootViewController to the Tab Controller, this allows me to use the tab functionality but I still don't have access to the Nav Controller. Same goes for the Nav Controller, it works when it is the root view controller but there is no Tab Controller functionality
Edit 2: In the viewDidLoad() of the mainSearchViewControllernavigationController?.isNavigationBarHidden = false` and I have the root view controller as the Tab Bar Controller. I can access the tab icons, and I can see the navigation bar, but the navigation functionality when tapping on a cell doesn't work.
Changing rootViewController is likely causing your problem since tabViewController probably doesn't have loginViewController as a tab.
If you need to display loginViewController based on an if statement, it's better practice to show it using something like:
navViewController.pushViewController(loginViewController, animated: true)
I am using navigationController?.popToRootViewController(animated: true) to dismiss my current view to the previous view. My view controller relationship looks like this.
VC1->VC2
VC1->VC3
VC3->VC2
Whenever the client is in VC2 I want to pop the navigation controller back to VC1. This works fine when the rootviewcontroller is set to VC1. However, when the client uses a segue from VC3 to enter VC2, the rootviewcontroller is set to VC3 and the navigation controller pops to VC3.
I tried to change the rootviewcontroller like this.
// set root view controller
let appdelegate = UIApplication.shared.delegate as! AppDelegate
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC1 = mainStoryboard.instantiateViewController(withIdentifier: "VC1") as! FirstViewController
appdelegate.window!.rootViewController = VC1
navigationController?.popToRootViewController(animated: true)
But this actually returns the viewcontroller to the root view controller (VC1) even before the line "navigationController?.popToRootViewController(animated: true)" is executed so there is no animation.
Is there any way to set the rootviewcontroller of a navigation controller without presenting the root view controller right away?
If you put appdelegate.window!.rootViewController = VC1, the stack of controllers has died, you only have in the stack an alone Controller, therefore you can't apply popToRootViewController.
If this is your required navigation, maybe, this post helps you:
I'm trying to trigger a modal view from the AppDelegate every time the app is opened. I can see my breakpoint being hit, but the modal never shows. I'm including an image of my storyboard in case it matters. It's a fairly simple app right now with a 2 tab tab bar controller.
This is the code I have in the AppDelegate to trigger it.
let newVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginView")
let view = window?.rootViewController as! UITabBarController
view.selectedViewController?.show(newVC, sender: nil)
It seems newVC is not in the tab bar's controllers array, and you're attempting to modally present it from the selectedViewController in AppDelegate where there probably isn't a selected view controller yet.
One solution is to present newVC after viewDidLoad of the selected view controller (view controller at selectedIndex). If the presentation must take place before any of the tab bar's view controllers are loaded, then you might wanna set it as the window's root view controller and set the root to be the tab bar once newVC has finished its business.
We are looking to change the way a user logs out of our app. In order to do that, we want to dismiss all the VCs below the current VC and put another VC on top as the root VC. Right now we are doing this which I believe does not dismiss any VC below from memory.
let viewController = storyboard?.instantiateViewController(withIdentifier: "SignIn")
if let unwrappedViewController = viewController {
self.present(unwrappedViewController, animated: true, completion: {})
}
The problem is that the VC that we want to put on top is not embedded in a Navigation Controller or tab bar controller. How would we dismiss the VCs and set the new VC as the main VC as if the user was opening the app for the first time without having previously logged in? We also do want the transition to be animated with whatever animation is normal for that event (modal animation is fine). I have read a bunch of different ways on doing it but I want to know which way is best practice and should be implemented specifically dismissing all VCs and putting a new VC that isn't in a Nav controller on top.
If you can access the UIWindow of the app, you can set its rootViewController property to your sign-in view controller, effectively removing all current view controllers and adding the sign-in view controller instead. Here's an example:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
// Should remove all subsequent view controllers from memory.
appDelegate.window?.rootViewController.dismiss(animated: true, completion: nil)
// Set the root view controller to a new instance of the sign in view controller.
appDelegate.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SignIn")
So i have an app were you're supposed to chose a location from a map but the way i'm doing it is by a popup off a view controller that has a MapKit and a search bar and a button for choosing users location, the problem lies on when i'm done with the View I normally called the
self.view.removeFromSuperview()
but the thing that is being remove is the View controller itself but il leaves behind the navigation bar and it doesn't let me do nothing else (in fact when I go to another tab and return to the same one the VC returns).
the way i'm instantiating the VC is like this:
func location ()
{
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "mapV") as! UBICACIONViewController
let nav1 = UINavigationController()
nav1.viewControllers = [vc]
self.addChildViewController(nav1)
self.view.addSubview(nav1.view)
vc.delegate = self //this for the info that i'm getting afte(works fine)
nav1.didMove(toParentViewController: self)
}
So until here works fin but when the user pick te button that says "Use My Location" and tries to return there's the problem.
in my VC view controller this is what happens when finished:
func Remove()
{
if let del = delegate {
del.dataChanged(str: Event.sharedInstance.Location!)
}
self.view.removeFromSuperview() //Problem Here :/
}
Second VC
Return to First VC
Actually you have added a child view controller nav1. And nav1 has a subview nav1.view.
while removing you are removing this view from its superview, not the child view controller added.
Any ways, From you screen short..I understand that the pop up view is off full screen then why dont you present this VC modally, or else push it to navigation stack.