I'm having a trouble with my very simple app.
My app simply have 2 mains UIViewControllers called News and Category, each of them has their own UINavigationController.
So in AppDelegate.swift, I've done like this
window = UIWindow(frame: UIScreen.main.bounds)
let tabBarController = UITabBarController()
let newsVC = NewsVC()
// CREATE TAB BAR ITEM WITH TITLE ONLY
let newsVCTabBarItem = UITabBarItem(title: "News", image: nil, tag: 1)
newsVC.tabBarItem = newsVCTabBarItem
let categoryVC = CategoryVC()
// CREATE TAB BAR ITEM WITH TITLE ONLY
let categoryVCTabBarItem = UITabBarItem(title: "Category", image: nil, tag: 2)
categoryVC.tabBarItem = categoryVCTabBarItem
let rootViewControllers = [newsVC, categoryVC]
// CREATE NAVIGATION CONTROLLER FOR EACH OF THEM
tabBarController.viewControllers = rootViewControllers.map {
UINavigationController(rootViewController: $0)
}
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
When I run this simple application, the tab bar items do not show anything :(
But when I change the UITabBarItem to system's styles like this
let newsVCTabBarItem = UITabBarItem(tabBarSystemItem: .featured, tag: 1)
It's working perfectly! So hard to understand!
So does anyone know why my title-only tab bar item does not working? Have I missed something important?
Thanks in advance!
Add title property to the ViewControllers to show the title in UITabBarItem.
var title: String? { get set }
Set the title to a human-readable string that describes the view. If
the view controller has a valid navigation item or tab-bar item,
assigning a value to this property updates the title text of those
objects.
let newsVC = ViewController()
newsVC.title = "News"
.....
let categoryVC = ViewController2()
categoryVC.title = "Category"
.....
Or
Assign an image to the UITabBarItem to see the result.
let newsVCTabBarItem = UITabBarItem(title: "News", image: UIImage(named: "news.png"), tag: 1)
....
let categoryVCTabBarItem = UITabBarItem(title: "Category", image: UIImage(named: "category.png"), tag: 2)
.....
Update:
Tab bar items are configured through their corresponding view
controller. To associate a tab bar item with a view controller, create
a new instance of the UITabBarItem class, configure it appropriately
for the view controller, and assign it to the view controller’s
tabBarItem property. If you don't provide a custom tab bar item for
your view controller, the view controller creates a default item
containing no image and the text from the view controller’s title
property.
Related
I have recently been working on making my application programmatically and I have run into an error, where the tab bar controller is producing a navigation controller as well (which I want for every page), however I am struggling to find a way to add a button onto the bar, like a done button in the top right, from the ViewController itself - not the tab bar controller.
Basically I am trying to add a submit button which will perform an action as declared in the ViewController, however the only way I have found is to either make the navigation controller individually in each View controller which would be less efficient or to add the button in the Tab Bar controller, but that would mean copying the function (which is specific to other items on the View controller page) into the tab bar controller which would be a nightmare.
This is the code on the Controller - newReportScreen in the viewDidLoad function
//setting the nav bar submit button
let navBar = UINavigationBar()
view.addSubview(navBar)
let submitItem = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(SubmitButtonTouched))
let navItem = UINavigationItem(title: "Submit")
navItem.rightBarButtonItem = submitItem
navBar.setItems([navItem], animated: false)
which is conflicting with the code in the TabBar controller
//setting up the variable for the first view controller which will be used for the tab bar
let firstViewController = MapVC()
//set the nav title
firstViewController.title = "Home"
//initialising the first tab bar item, which will have the title of Home, the image named below and the tag number, showing the position on the bar
firstViewController.tabBarItem = UITabBarItem(title: "Home", image: UIImage(named: "Home.png"), tag: 0)
//initialising the second of the view controllers which will be used to access from the tab bar controller
let secondViewController = localReportsTVC()
//set the nav title
secondViewController.title = "Local Reports"
//setting the second view controller on the tab bar and giving it a title, image and location on the bar
secondViewController.tabBarItem = UITabBarItem(title: "Local Reports", image: UIImage(named: "Local.png"), tag: 1)
//setting up the third view controller to be referenced on the tab bar controller
let thirdVC = NewReportScreen()
//set the nav title
thirdVC.title = "New Report"
//setting the third view conteroller to be on the tab bar with the image, name and the location on the bar in relation to the other items
thirdVC.tabBarItem = UITabBarItem(title: "New Report", image: UIImage(named: "Plus Icon.png"), tag: 2)
//setting up the third view controller to be referenced in the tab bar controller
let fourthVC = MyReportsTVC()
//set the nav title
fourthVC.title = "My Reports"
//setting the third item on the tab bar up so that it has a position, image and title
fourthVC.tabBarItem = UITabBarItem(title: "My Reports", image: UIImage(named: "MyReports.png"), tag: 3)
//setting up the fifth section of the tab bar, where it will be referenced later
let fithVC = SettingsScreen()
//set the nav title
fithVC.title = "Settings"
//setting up the fifth item, so that it has a title, image and position on the bar
fithVC.tabBarItem = UITabBarItem(title: "Settings", image: UIImage(named: "Settings Icon.png"), tag: 4)
//initialising the final tab bar wih all of the elements from above
let tabBarList = [firstViewController, secondViewController, thirdVC, fourthVC, fithVC]
//setting the view controllers equal to the tab bar list defined above - also adding in the navigation controller to each of the tabs so that they have a title and also a navigation controller to add the back button in
viewControllers = tabBarList.map { UINavigationController(rootViewController: $0)}
I may be overthinking this, but I have struggled to work around this, so any help is greatly appreciated!
When creating your UITabBar you're already adding UINavigationController's.
viewControllers = tabBarList.map { UINavigationController(rootViewController: $0)}
This is correct.
Then in each View Controller, you can set its navigation bar items via UIViewControllers navigationItem property:
//setting the nav bar submit button
let submitItem = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(SubmitButtonTouched))
self.navigationItem.rightBarButtonItem = submitItem
doing this in viewDidLoad is correct.
I have a tabBarController under which I loaded some navigation controller. But the title of my navigation controller is not showing up. Even I noticed if I add buttons in my left and right navbar item, it doesn't show up as well.
My Tabbar Setup:
let quranNavigationController = UINavigationController()
// quranNavigationController.navigationBar.titleTextAttributes = textAttributes
// quranNavigationController.navigationBar.tintColor = Colors.primaryTextColor
// quranNavigationController.navigationBar.barTintColor = Colors.navigationBackgroundPrimary
let quranSnackBarController = AppSnackbarController(rootViewController: quranNavigationController)
quranSnackBarController.shouldExtend = false
quranSnackBarController.tabBarItem = UITabBarItem(title: "Books".localized(), image: UIImage(named: "quran"), tag: 1);
let menuViewController = TestViewController()
let quranController = QuranPrintController()
let sideMenuController = SideMenuController(contentViewController: quranController, menuViewController: menuViewController)
quranNavigationController.viewControllers = [sideMenuController]
tabBarController.viewControllers = [homeNavigationController2, quranSnackBarController, prayerNavigationController, duaSnackBarController]
window.rootViewController = tabBarController
QuranPrintController:
class QuranPrintController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Quran"
self.navigationItem.title = "Prayer Times"
}
}
This is what my output is. No title is shown. I tried adding buttons on the left and right sides. But didn't work as well
First make sure your tabbarcontroller is embedded with navigation controller and then your all controllers are connected with your navbar
like this
then embed your navigation controllers with your each view controller with seperate navigController
Try Add this in your ViewdidLoad() .
self.view.bringSubviewToFront(yourbuttonname)
I have a tab bar controller and I have added five view controllers in it like this:
class InfluencerMainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let findWorkVC = UINavigationController.init(rootViewController: InfluencerFindWorkVC.instantiate(fromAppStoryboard: .Influencer))
findWorkVC.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "ic_home"), tag: 0)
let inboxVC = UINavigationController.init(rootViewController: InfluencerInboxVC.instantiate(fromAppStoryboard: .Inbox))
inboxVC.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "ic_inbox"), tag: 1)
let workDiaryVC = UINavigationController.init(rootViewController: InfluencerWorkDiaryVC.instantiate(fromAppStoryboard: .Influencer))
workDiaryVC.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "ic_work"), tag: 2)
let notificationsVC = InfluencerNotificationsVC.instantiate(fromAppStoryboard: .Influencer)
notificationsVC.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "ic_notification"), tag: 3)
let accountVC = InfluencerProfileVC.instantiate(fromAppStoryboard: .Influencer)
accountVC.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "ic_profile"), tag: 4)
let tabBarList = [findWorkVC, inboxVC, workDiaryVC, notificationsVC, accountVC]
viewControllers = tabBarList
self.tabBar.tintColor = UIColor.appPurpleColor
self.tabBar.barTintColor = UIColor.white
}
}
Problem is my first controller, which is findWorkVC, its viewWillAppear is getting called but when I click on any other view controller, their viewWillAppear are not getting called.
It is working fine pre iOS 13 devices but on iOS 13 its not just getting called and also the height of navigation bar is lesser than iOS 12's navigation bar height, you can see the title in navigation bar is just overlapping the status bar text.
I created a new project and tested out everything, view controllers with tabs, everything was working there but not in my project so I started looking for the things which were different in my project than a newly created project.
Turns out, it was the root view controller. I was setting root view controller like this with animation
let controller = InfluencerMainTabBarController.instantiate(fromAppStoryboard: .Main)
UIView.transition(from: self.view, to: controller.view, duration: 0.6, options: [.transitionFlipFromTop], completion: { completed in
UIApplication.shared.keyWindow?.rootViewController = controller
})
So I simply presented the view controller with modalPresentationStyle = .fullScreen without animation and everything worked.
let controller = InfluencerMainTabBarController.instantiate(fromAppStoryboard: .Main)
controller.modalPresentationStyle = .fullScreen
DispatchQueue.main.async { UIApplication.shared.keyWindow?.rootViewController = controller }
Now I only have to look for how to set root view controller with animation. :|
If your presentation style is not the new default by Apple (sheet), than just set the presentation style for all your ViewControllers (NavigationController included) to FullScreen. This way the viewWillAppear method will be called again for every VC.
I have subclassed UITabBarController to allow for some customization specific to my app. It is the root view controller of my UIWindow and displays itself correctly on launch, and even shows the correct tab's view hierarchy as well.
The problem is with the selected tabbar item's tint color. Inside viewDidLoad of the custom tab bar controller subclass, I have set both the unselected and selected tint colors for the tab bar. See below:
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .tabBarItemActiveTint
tabBar.unselectedItemTintColor = .tabBarItemInactiveTint
tabBar.barTintColor = .tabBarBg
let dashboardVC = DashboardViewController.build()
let settingsVC = SettingsTableViewController.build()
let settingsNavC = UINavigationController(rootViewController: settingsVC)
settingsNavC.navigationBar.barStyle = .black
viewControllers = [dashboardVC, settingsNavC]
selectedViewController = dashboardVC
// Accessing the view property of each tab's root view controller forces
// the system to run "viewDidLoad" which will configure the tab icon and
// title in the tab bar.
let _ = dashboardVC.view
let _ = settingsVC.view
}
As you can see, the controller has its child view hierarchies set, and the views are loaded at the bottom so their respective viewDidLoad methods run where I have code that sets the tabBarItem. Here's an example from the dashboard view controller:
tabBarItem = UITabBarItem(title: "Dashboard", image: UIImage(named: Theme.dashboardTabBarIcon), tag: 0)
Everything about this works except for the selected icon and title. When the app launches I can see the tab bar, the first view hierarchy (the dashboard) is visible onscreen and the tabs all function properly. But the dashboard's icon and title are in an unselected state. I have to actually tap the tab bar icon to get the state to change such that it is selected.
Once you tap one of the tabs, the selected state works as normal. The issue is only on the first presentation of the tab bar.
Here is an image showing the initial state of the tab bar on launch. Notice the dashboard icon is not selected, even though it is the presented view controller.
UPDATE
Skaal's answer below solved the problem for me.
For future reference: the key difference between the code presented here in my question and his sample in the answer is that the tabBarItem is set in viewDidLoad of his custom TabBarController class. By contrast, that code was placed within the viewDidLoad method of each constituent view controller class in my project. There must be a timing issue of when things are called that causes the tint color to not be set in one scenario and work properly in the other.
Key Takeaway: If you set up a tab bar controller programmatically, be sure to set your tabBarItem properties early on to ensure tint colors work properly.
You can use :
selectedIndex = 0 // the index of your dashboardVC
instead of selectedViewController
EDIT - Here is a working sample of UITabBarController:
class TabBarController: UITabBarController {
private lazy var firstController: UIViewController = {
let controller = UIViewController()
controller.title = "First"
controller.view.backgroundColor = .lightGray
return controller
}()
private lazy var secondController: UIViewController = {
let controller = UIViewController()
controller.title = "Second"
controller.view.backgroundColor = .darkGray
return controller
}()
private var controllers: [UIViewController] {
return [firstController, secondController]
}
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .magenta
tabBar.unselectedItemTintColor = .white
tabBar.barTintColor = .black
firstController.tabBarItem = UITabBarItem(title: "First", image: UIImage(), tag: 0) // replace with your image
secondController.tabBarItem = UITabBarItem(title: "Second", image: UIImage(), tag: 1) // replace with your image
viewControllers = controllers
selectedViewController = firstController
}
}
Im doing an application which is a tab bar controller app with tab bar items (Search, Login, More). After successful login of user tab bar changes to (Search, Account, More) with a Logout button in More. The problem is when Logout is clicked it should load SearchViewController and if again Login is clicked in tab bar the corresponding Login page doesn't show. It shows a black screen.
#IBAction func btnLogOutClicked(_ sender: UIButton)
{
let hm = SearchVC()
let tabOneBarItem = UITabBarItem(title: "Search", image: UIImage(named: "TabHome.png"), selectedImage: UIImage(named: "TabHome.png"))
hm.tabBarItem = tabOneBarItem
let lgn = LoginVC()
let tabOneBarItem1 = UITabBarItem(title: "Login", image: UIImage(named: "tabAcc.png"), selectedImage: UIImage(named: "tabAcc.png"))
lgn.tabBarItem = tabOneBarItem1
let mropt = MoreVC()
mropt.tabBarItem = UITabBarItem(tabBarSystemItem: .more, tag: 2)
let controllers = [hm, lgn, mropt]
self.tabBarController?.viewControllers = controllers
self.tabBarController?.selectedIndex = 0
}
How would I make a tab bar controller display when Logout is clicked and clicking the tab bar item should display corresponding view controller.
You have to load every vc like this after giving each one a storyboard identifier
let hm = storyBoard.instantiateViewController(withIdentifier: "SearchVC") as! SearchVC
This
let hm = SearchVC()
Is used when the vc is created programmatically , and it's the main reason for black screen as the vc is loaded without it's layout
There are some ways to load view of view controllers:
from nib files (use public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?))
from storyboards (use UIStoryboard to instantiate view controller)
load it directly (usually programmatically) by overriding loadView()
The default init method such as SearchVC() will call init(nibName: nil, bundle: nil) for you. What's going on in this case is here.
You have to use only first two option to load view from nib or storyboard correctly.
View Management is here to see more.