I make an app that has 3 tabs. I want to make my tab bar controller dynamically change the view controller of the first tab bar according to the logged in user type. The rest two tab bars are static. For example, if the user type is 1, I want to show ViewController1 for the first tab, and if the user type is 2, I want to show ViewController2 instead. Is this impossible to achieve when the tab bar is designed in the storyboard? I use storyboard in the app.
You can change these by using the function on a tabview controller setviewcontrollers (see documentation here: https://developer.apple.com/documentation/uikit/uitabbarcontroller/1621177-setviewcontrollers).
For instance I have a storyboard with 4 Viewcontrollers in a tabviewcontroller, and then in the first viewController that loads, I added the following in viewDidLoad()"
let storyboard = UIStoryboard(name: "myStoryboard", bundle: nil)
let newTab = storyboard.instantiateViewController(withIdentifier: "testControllerID") as! TestViewController
let myControllers = [newTab, tabButton2, tabButton3, self, tabButton4]
tabBarController?.setViewControllers(myControllers, animated: true)
So I was able to rearrange my previously defined tabs as well as add "newTab", which is a VC designed on the storyboard, given an identifier, but not actually added to the tabViewController.
Hope that helps
Related
I have a navigation controller and several view controllers. When I click on a table cell in the Stocks View Controller, I open the Stock Chart View Controller. In it I have 3 buttons "Chart", "Summary", "News". I want to click on "Summary" to move to the Stock Summary View Controller, but an error occurs for several dozen lines. How can I implement transitions in such a menu?
And how do I click on" back" in Stock Chart View Controller and Stock Summary View Controller to return to Stocks View Controller?
Code for switching from the Stock Chart View Controller to the Stock Summary View Controller:
#IBAction func onSummaryButtonTapped(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let viewController = storyboard.instantiateViewController(identifier: "StockSummaryViewController") as! StockSummaryViewController
navigationController?.pushViewController(viewController, animated: true)
}
Storyboard:
enter image description here
Your App:
NavigationVC > StocksVC (tapping on a cell) ➡ StockChartVC > StockSummaryVC > etc.
➡ is important here, how do you navigate from StocksVC to StockChartVC? Do you push StockChartVC or present modally? Since you do segues programmatically, it can't bee seen in the image.
I can provide the following info to help you:
The push-style navigation automatically contains/adds controls for backward navigation because it adds all ViewControllers to its navigation stack; all ViewControllers will be embedded in the same NavigationController
In case of presenting scenes modally, these ViewControllers have to have their own NavigationController. thus, your StockChartVC has to be embedded in its own NavigationController
I believe you do the push-style segue from StocksVC to StockChartVC and also pushing from StockChartVC to StockSummaryVC; in that case, there should be no problem at all. Moreover (as I wrote), all they VCs will be added controls for backward navigation. Everything will be working just fine!
So pay attention how you are segueing and according to that, implement the right navigation.
I'm building an app and I've decided to build my user interfaces in multiple Storyboard files. Each Storyboard is a group of similar actions. For example, LogInStoryboard contains views for logging in and for registering a user.
I've run into a bit of problem with navigating to a new Storyboard and dismissing all the previous view controllers to clean up after myself.
This is my dataflow:
1) App launches into a log in view. Root view controller is LogInViewController, which lives in LogInStoryboard.
2) User taps the register button to summon the modal RegistrationViewController, also lives in LogInStoryboard.
3) User completes registration and is automatically signed in.
At this point, the view controller stack is [LogInViewController, RegistrationViewController]. After registering, the user is automatically signed in so I want to navigate to their home screen, HomeViewController. However, HomeViewController lives inside another Storyboard HomeStoryboard.
I want to dismiss both LogInViewController and RegistrationViewController, and then I want to instantiate HomeViewController from HomeStoryboard and present it. This way I have a simple view controller stack and the LogInStoryboard view controllers can all be deallocated.
What would be the best way to achieve this sort of flow? Or should I even be so worried about those old view controllers?
Its very often the case that your login flow acts almost as an entirely separate app who's only function is to authenticate the user and write the user info to your database. In this case when you are done with the login instead of performing a segue what you want to do is replace the entire view hierarchy with HomeViewController. To do this you can simply call UIApplication.shared.keyWindow! = UIStoryboard(name: "HomeStoryboard", bundle: nil).instantiateInitialViewController() (assuming you made HomeViewController the initial view controller). Of course this has no animation and is kind of ugly so you way want to use UIView.transition(with:duration:options:animations:completion:) to animate the change instead. Example:
UIView.transition(with: UIApplication.shared.keyWindow!, duration: 0.25, options: .transitionCrossDissolve,
animations: {
UIApplication.shared.keyWindow!.rootViewController = UIStoryboard(name: "HomeStoryboard", bundle: nil).instantiateInitialViewController()
})
Swift 4.0
I would start with the HomeViewController as the Initial View Controller.
And from there create the LogInViewController.
if shouldLogin {
let storyboard = UIStoryboard(name: "LogInStoryboard", bundle: nil)
let loginVCtrl = storyboard.instantiateViewController(withIdentifier: "yourLogInViewControllerID")
present(loginVCtrl, animated: true)
}
And after your user finished the Login/Registering process you can call:
dismiss(animated: true)
So your ViewController stack looks like:
[HomeViewController]
[HomeViewController, LogInViewController]
[HomeViewController]
set your HomeViewController in appdelegate as navigationcontroller and set navigationcontroller as rootviewcontroller
NOTE: Both actions perform in appdelegate
I have three storyboards in my project. There is a main and two separate workflow storyboards. Each storyboard is embedded in its own navigation controller.
Since I have broken up the storyboards into workflows I have to programmatically add each workflow to the tab bar. This is working correctly.
My issue occurs when I try and push a view (within a workflow) onto the workflows navigation controller. It seems that the navigation controller for a workflow is never being used. I verified this by changing the navigation bar color for each workflow.
I have tried two options, each set up on a workflow.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let mainStoryboard = UIStoryboard(name: "Workflow 1", bundle: nil)
let actionItemStoryboard = UIStoryboard(name: "Workflow 2", bundle: nil)
let workflow1Controller = mainStoryboard.instantiateViewController(withIdentifier: "navigationController1")
let workflow1TabItem = UITabBarItem(title: "Item 1", image: nil, selectedImage: nil)
workflow1Controller.tabBarItem = workflow1TabItem
let workflow2Controller = actionItemStoryboard.instantiateViewController(withIdentifier: "workflow2")
let workflow2TabItem = UITabBarItem(title: "Item 2", image: nil, selectedImage: nil)
workflow2Controller.tabBarItem = workflow2TabItem
self.viewControllers = [workflow1Controller, workflow2Controller]
}
Option 1
Setting the tab view controller to point to the navigation controller (pretty sure this is incorrect but was a test I did). This displays the views how I want but doesn't show the navigation item (back button).
Option 2
Setting the tab view controller to point to the list view (see screen shots below). This displays the back button but upon clicking on the cell the last view is displayed "over" the tabs.
Main Storyboard
Workflow 1 Storyboard
Workflow 2 Storyboard
In order to solve this I ended up doing the following:
Remove the navigation controllers from the workflows
Create a "MockTabController" (just a UIViewController with a UITabBar placed on the bottom of the view)
Now that I have a UIViewController with a UITabBar I can have each workflow view extend this view instead of a UIViewController and thus a tab bar is consistent throughout my app (where I want it).
For instances where the workflow has a UITableViewController I simply embed the UITableViewController inside a ViewController as a child view. The ViewController then extends my MockTabController and the result is a TableViewController with a tab bar that doesn't need any modifications to work.
In order to simplify the navigating throughout the app, I simply reset the navigation stack back to the beginning of the tab controller. Clicking a tab bar item unwinds all the workflow and then pushes the start of a new workflow.
I am trying to leave the initial view controller, and go into the blank view controller. That is fine, but that would make the blank view controller also part of the navigation controller, which is not what I want. I want to segue out of the view controller.
In the view controller I try to segue out of, it pops it self, and when I try the method in the view will appear of the target view controller, self.navigationController?.topViewController returns itself, but self.navigationController?.popViewControllerAnimated(animated) doesn't work
If you have a navigationController do
self.navigationController?.popViewControllerAnimated(false)
Otherwise do
self.dismissViewControllerAnimated(false, completion: nil)
Update
Go to your Storyboard, select the ViewController you want to navigate to and add a storyboard id. Make sure the click "Use Storyboard ID"
Go to the class you want to navigate from and add the following code
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// vc is the Storyboard ID that you added
// as! ... Add your ViewController class name that you want to navigate to
let controller = storyboard.instantiateViewControllerWithIdentifier("vc") as! ViewController
self.presentViewController(controller, animated: true, completion: { () -> Void in
})
Add this code in the action that you use when you want to navigate.
I believe this can be done without having to write any code at all. To Segue out of a Navigation Controller follow these steps:
Create a Segue from the View Controller that has a Navigation Controller as it's root view controller to the View Controller that you would like to go to next (without being inside the Navigation Controller)
Click on the new Seque
Open the Attributes Inspector
Choose 'Kind' -> 'Present Modally'
Choose 'Present' -> 'Over Current Context'
I have a storyboard set up with a tab bar controller and three tabs. Each tab has a navigation controller. The first tab has three scenes. There is a button (log out) in a view on the third tab that I would like to segue to the second scene on the first tab (corresponding to the log in view controller and connected to the first scene via Show(e.g., Push).
Here is what I've tried:
self.tabBarController?.selectedIndex = 0
This works, insofar as I get back to the first tab's initial scene after tapping the UIButton. But since I want to get to the second scene, this is not a complete solution. I think the solution may be along the lines of:
self.tabBarController?.selectedViewController = LoginViewController()
or
self.tabBarController?.setViewControllers(self.LoginViewController, animated: true)
But I do not want to create another instance of a view controller.
Can I still use .selectedIndex to implement a solution?
A simple solution u can try is
1. Set a Global variable (i.e in App Delegate) name as isLogoutClick of type boolean.
2. While you are on third tab and click on logout button then make the global variable "isLogoutClick" as true.
3.and then navigate to first tab (1st scene) and on viewDidLoad just check the condition that
if(appDelegate.isLogoutClick)
{
push your view to next scene.
}
4. make false the value of isLogoutClick.
5. make sure at initially the value of isLogoutClick is false.
try this might it will help you.
After setting selectedIndex to 0, perform the segue you want (in this example, "loginSegue"). You can name your segue in the storyboard if you haven't already.
tabBarController?.selectedIndex = 0
if let someViewController = tabBarController?.viewControllers?[0] as? SomeViewController {
someViewController.performSegueWithIdentifier("loginSegue", sender: nil)
}
I'm not sure if this works for tabBarController because I've used this for my navigationController but should work the same.
if let tab = self.tabBarController?.viewControllers {
if let index = find(tab.map { $0 is LoginViewController }, true) {
let destination = tab[index] as LoginViewController
tabBarController?.presentViewController(destination, animated: true, completion: nil)
}
}
With a navigationController I would use popToViewController but I'm not sure how the tabBarController exactly works