This question already has answers here:
Change initial view controller after first launch of app
(2 answers)
Closed 5 years ago.
The starting point for my app is a simple login screen. If the user wants to make a new account, then they will segue into a navigation controller which manages all the views for creating a new account. The problem is that after they have made the account and signed in, the navigation bar is still there (pressing the back button will bring them to the "enter a password" page). I don't want to simply hide the navigation bar, I want the navigation controller and it's whole stack of views to be gone completely.
First of all after a successfull registration don't wait the user to click back or any thing automatically direct it to the main content VC of the app by changing the root of the AppDelegate's window to the mainVC embeded in a navigation controller , so later you can push other VCs to it like user settings , about VCs and so on
first create a new nav
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"mainView") as! mainViewController
let navController = UINavigationController.init(rootViewController: viewController)
and assign it to root
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = navController
You can Remove login screen from navigation controller using below code:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
var tempVCs = self.navigationController?.viewControllers
for tempVC: UIViewController in tempVCs {
// remove login screen from navigation stack
if (tempVC is LoginScreenVC) {
tempVC.removeFromParentViewController()
}
}
}
Related
This question already has answers here:
How to push and present to UIViewController programmatically without segue in iOS Swift 3
(3 answers)
Closed 3 years ago.
My problem is that I can't find the correct code to transition between two scenes in IOS programatically. I have tried a few example codes, but none of them work as expected.
let vc storyboard!.instantiateViewController(withIdentifier: "savedMode") as! SavedModeViewController
let navigationController = UINavigationController(rootViewController: vc)
self.present(navigationController, animated: true, completion: nil)
This code goes to the new scene, but the back button doesn't appear in the left side of the navigation bar, and transition between scenes is wrong; instead of left to right animation, there is bottom to top animation.
let controller = storyboard?.instantiateViewController(withIdentifier: "savedMode") as! SavedModeViewController
present(controller, animated: true, completion: nil)
This is the second code I have tried. The problem is that with this code, the navigation bar doesn't show at all in the second scene.
I have tried another way, but instead of second scene, the screen just turned black.
Calling present will present the new view controller modally over the current view controller.
What you want is to push the new view controller onto the existing UINavigationController with pushViewController .
if let savedVC = storyboard?.instantiateViewController(withIdentifier: "savedMode") as? SavedModeViewController,
let navigationController = self.navigationController {
navigationController.pushViewController(savedVC, animated: true)
} else {
// TODO: Handle unexpected nil occurrences
print("Error loading SavedModeViewController")
}
I embedded the LoginVC a NavigationController and on log-in, push the user to SwitchboardVC.
If the user is already logged in, I bypass the Login Screen with this code in the AppDelegate in didFinishLaunchingWithOptions:
if Auth.auth().currentUser != nil
{
let mainStoryBoard: UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
let nextView: SwitchboardVC = mainStoryBoard.instantiateViewController(withIdentifier: "SwitchboardVC") as! SwitchboardVC
self.window?.rootViewController = nextView
}
When I have to login, the NavigationBar shows on all screens but if I am already logged in, the navigation bar is missing on all other screens.
I haven't been able to find a solution on Google yet but I am assuming that I am not searching for the correct term.
Any help is greatly appreciated
I believe it is because you are setting your root as the newView rather than pushing the newView from the navigationController, which would add it to the navigation stack and keep the bar at the top.
For the navigation hierarchy here is some reading:
https://medium.com/#strawb3rryx7/swift-the-hierarchy-of-uinavigationcontroller-programmatically-91631990f495
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")
I'm currently writing an app where we are launching a walkthrough the first time the user opens the app. At the end of it, we require the user to fill in a few details, and to do so I would like him to press a button to get redirected to the settings page.
The thing is, this page is one level down the navigation controller (from the landing page). As it stands, I can correctly instantiate the landing page, but the redirection to the settings page never happens.
let mainView = self.storyboard?.instantiateViewControllerWithIdentifier("NavCtrl")
self.presentViewController(mainView!, animated: false, completion: nil)
// the above works correctly and sends us to the landing screen
// (rootView of the navigation controller)
// the following lines never have any effect though
let settingsView = self.storyboard?.instantiateViewControllerWithIdentifier("Settings View")
self.navigationController?.pushViewController(settingsView!, animated: false)
I think it's because I'm trying to call .pushViewController before the storyboard had time to instiate either the first or second view.
So I have a few questions:
Is there a way to, indeed, instantiate a view and then navigate to another one right after it has been instantiated ? (this in order to keep the navigation stack and maintain accurate nav bar behaviour)
If there isn't, would it be possible to programmatically populate the navigation stack so that I would only need to instantiate the settings view ? This in order to still have the back button in the nav bar that would send to the landing screen ?
Thanks in advance guys
Ok thanks to #Leonardo for showing me the correct direction !
I solved the issue by doing the following in appDelegate:
/*
* Override window root view and set it to a newly initialized one
* view: StoryboardID of view to display
* navigateTo: if true, set the root view to NavCtrl and then navigate to the desired view
*/
func setWindowViewTo(view: String, navigateTo: Bool) {
//initalize storyboard & window programmatically
window = UIWindow.init(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
//if true, navigate from landing page to specified view through navigationController
if(navigateTo) {
//instantiate the navigation controller
let navCtrl = storyboard.instantiateViewControllerWithIdentifier("NavCtrl") as! UINavigationController
//instantiate the landing page & the page we wish to navigate to
let landingView = storyboard.instantiateViewControllerWithIdentifier("Main View")
let vc = storyboard.instantiateViewControllerWithIdentifier(view)
//manually set the navigation stack to landing view + view to navigate to
navCtrl.setViewControllers([landingView, vc], animated: false)
//replace the rootViewController to the navigation controller
window!.rootViewController = navCtrl
//make it work
window!.makeKeyAndVisible()
} else {
window!.rootViewController = storyboard.instantiateViewControllerWithIdentifier(view)
window!.makeKeyAndVisible()
}
}
The important step was to indeed force downcast as! UINavigationController when instantiating the NavigationController with the storyboard.instantiateViewControllerWithIdentifier() method.
Then it's just a case to correctly instantiate the views of the navigation stack you want, and finally calling navCtrl.setViewControllers([view1, view2], animate: false).
Thanks all for your help !
You can use the - setViewControllers:animated: method to set the navigation stack of a UINavigationController
Here's the reference
But I don't think that's your problem, if I undestood your code correctly, it should be
//This is the navigation controller
if let mainView = self.storyboard?.instantiateViewControllerWithIdentifier("NavCtrl"){
//Nav being modally presented
self.presentViewController(mainView, animated: false, completion: nil)
// Instantiate the settings view
if let settingsView = self.storyboard?.instantiateViewControllerWithIdentifier("Settings View"){
//Push it to the navigation controller presented
mainView.pushViewController(settingsView, animated: false)
}
else{
//Can't Instantiate, deal with error
}
}
else{
//Can't Instantiate, deal with error
}
(reposting due to initial wrong title)
When users receive a push notification for our app (and they tap on it), the app should open up a details screen. From a navigation perspective, the app usually has this structure anytime the user opens it:
TabBarController -> Navigation Controller -> View Controller
Once a use open the push notification, I'd like to instantiate a UIViewController. However, this VC should be part of the tabbarcontroller, so that the user can navigate to other areas of the app as well. Right now I am able to display the VC itself, but I can't make it appear as part of the tabbarcontroller:
class private func instantiateJobDetailsViewController(job: JobModel) {
if let currentViewController = UIApplication.sharedApplication().delegate?.window??.rootViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let jobDetailViewController = storyboard.instantiateViewControllerWithIdentifier("JobDetailController") as! JobDetailController
jobDetailViewController.job = job
currentViewController.presentViewController(jobDetailViewController, animated: true, completion: nil)
} else {
ClientHelper.displayAlertAsync("Error", message: "Info for support: cannot present jobDetails view controller", controller: nil)
}
}
Can someone provide some guidance about how to instantiate such a VC and, at the same time, preserve the tab bar controller so the user can navigate within the app?
For reference, here is how I resolved this situation:
class private func instantiateJobDetailsViewController(job: JobModel) {
if let tabBarController = UIApplication.sharedApplication().delegate?.window??.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 0
let currentNavigationController = tabBarController.selectedViewController as! UINavigationController
let currentViewController = currentNavigationController.topViewController!
currentViewController.performSegueWithIdentifier("JobDetailSegue", sender: job)
} else {
ClientHelper.displayAlertAsync("Error", message: "Info for support: cannot present jobDetails view controller", controller: nil)
}
}
So it is basically:
Get the instance of the tab bar controller.
Make sure you are in a relevant screen on the tab bar controller by setting its selectedIndex property.
Get the instance of the navigation controller
Get the instance of the view controller being presented by the navigation controller.
In this way, I can open up a relevant screen from a push notification while a) showing the tab bar controller and b) preserving the navigation element to allow the user to navigate back from the screen.
You try change to structure
Navigation Controller -> TabBarController -> View Controller
You have a main navigationcontroller, set it is a global variable-a singleton ( var mainNavigation:UINavigationController?)
and when users receive a push notification, you can push to detailScreen from mainNavigation. Like this storyboard