I have an application with 5 tabs(and a UITabBarController). I have more view controllers which can be shown when user clicks buttons etc. Right now I'm trying to push a view controller in the first tab. When I use the following code, the new view controller completely replaces both the tab bar and the previous view controller so the only view controller that is shown is the new one and there's no way to go back. Here's my code:
#objc func openSettingsPage() {
let vc = UIStoryboard(name: "SettingsPage", bundle: nil).instantiateViewController(withIdentifier: "Settings") as! SettingsViewController
self.tabBarController?.show(vc, sender: self)
}
I've also tried:
let vc = UIStoryboard(name: "SettingsPage", bundle: nil).instantiateViewController(withIdentifier: "Settings") as! SettingsViewController
self.navigationController?.pushViewController(vc, animated: true)
Nothing happens when doing that. trying print(self.navigationcontroller) prints "nil" so apparently it doesn't find a navigation controller even though the view controller I'm writing the code in is embedded in a navigation controller
Related
I am using this code
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
self.navigationController!.pushViewController(controller, animated: true)
and also added:
self.tabBarController?.tabBar.isHidden = false
If I understand your hierarchy correctly you have a tab bar controller which has navigation controllers in it. So basically any of the tabs can push additional view controllers and tab bar is still visible.
Now you want to push some new controller on the currently selected view controller in the tab bar and you want to do it from another part of the app, another view controller that has no relation to tab bar.
The quickest way to do that is to expose a static instance of your tab bar view controller. This will only work if you always have only 1 tab bar controller in your application (probably 99% of the applications).
First add a current instance to your tab bar view controller:
class MyTabBarViewController: UITabBarController {
static private(set) var currentInstance: MyTabBarViewController?
override func viewDidLoad() {
super.viewDidLoad()
MyTabBarViewController.currentInstance = self
}
}
So when view loads a static value is assigned and can now be accessed anywhere in your project via MyTabBarViewController.currentInstance.
The rest is then just accessing the currently selected view controller and pushing a new view controller. Something like this should do:
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
(MyTabBarViewController.currentInstance?.selectedViewController as? UINavigationController)?.pushViewController(controller, animated: true)
You must push in TabBarController
self.tabBarController?.pushViewController(controller, animated: true)
I've separated my views into multiple storyboards to make managing the project as a team much easier. The only problem is that I want to have the navigation bar across all of my different view controllers. I embedded the first view controller in a navigation controller which added the navigation bar. In my second storyboard, I wanted to constrain a view to the navigation bar, however, since this view controller isn't embedded in a navigation controller, there isn't anything to constrain the view to. I put a navigation bar without a navigation controller to constrain to, however, when I perform the segue from the first storyboard, this results in two navigation bars. How do I make this so that I can constrain the view to the navigation bar without having two of them? Thanks in advance!
You can go from on view controller to another by three way:-
First using segue as you are already doing
second You can Present your view controller by
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
self.present(controller, animated: true, completion: nil)
Third You can Navigate View controller by
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
self.navigationController?.pushViewController(controller, animated: true)
I'm not sure your problem. I guess that you want to use ONE navigation bar to constrain many UIViewController in different Storyboard. Here is my answer.
Step 1: Set up Storyboard ID in each UIViewController
Step 2: push new UIViewController
func switchNextView() {
let storyboardName:String = "xxxxxx"
let storyboardId:String = "xxxxx"
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let vc: UIViewController =
storyboard.instantiateViewController(withIdentifier: storyboardId) as!
UIViewController
self.navigationController?.pushViewController(vc, animated: true)
}
I have created a TabBarController with 4 UIViewControllers. However, when I want to show one of the ViewControllers in the TabBarController with:
func gotoMainAppVC()
{
let mainVC:SearchVC = SearchVC()
self.present(mainVC, animated: true, completion: nil)
}
I only get a plain black screen. What am I doing wrong?
This is not how any of this works.
I am not sure what self is in your context but I expect it is either a tab bar controller or one of the view controllers on the tab bar.
By calling present it means you want to present a view controller on a current one which by default brings a new fullscreen view controller from bottom upwards.
Since you have created a new instance of view controller by calling SearchVC() this is not the view controller that you see in your storyboard. You may create any instances of any of your view controllers but in your case if you designed SearchVC in a storyboard you want to actually use that version of it. To do so you need to set storyboard id inside your storyboard. After you have done that you should initialise it as using:
UIStoryboard(name: <#T##String#>, bundle: nil).instantiateViewController(withIdentifier: <#T##String#>)
as in
let mainVC:SearchVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TheIDIHaveSet") as! SearchVC
But again this will not give you the view controller that is inside your tab bar but create a new instance. And it will not instert it as one of the tabs but present a new full screen view controller.
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.