I have a tab bar, whose items I want to change depending on a user's settings. So I need to be able to add/remove items from the tab bar programmatically. Here is my code:
class TabBarViewController: UITabBarController {
var controllerArray = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tabBar.tintColor = UIColor.greenColor()
let storyBoard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let controller1 = storyBoard.instantiateViewControllerWithIdentifier("Navigation2") as! nav2
let controller2 = storyBoard.instantiateViewControllerWithIdentifier("Explore") as! ExploreNavigation
let controller3 = storyBoard.instantiateViewControllerWithIdentifier("friends") as! ExploreNav
let controller4 = storyBoard.instantiateViewControllerWithIdentifier("controller3ID") as! controller3VC
controllerArray.append(controller1)
controllerArray.append(controller2)
controllerArray.append(controller3)
controllerArray.append(controller4)
self.tabBarController?.setViewControllers(controllerArray, animated: true)
}
}
but if I comment out one of the lines, for example like this:
//controllerArray.append(controller2)
so that that controller is not apart of the controllersArray, the controller still displays in the tab bar. What am I doing wrong?
I also tried this, and it didn't work either:
self.tabBarController?.viewControllers = controllerArray
Just say
self.setViewControllers(controllerArray, animated: true)
instead of
self.tabBarController?.setViewControllers(controllerArray, animated: true)
You are subclassing UITabBarController not UIViewController so you don't have, or at least don't need, the property tabBarController.
Related
I am failing to understand the fundamentals of what is needed to add my HomeViewController (UIViewController) as one of the tabs in my homeTabBarController (UITabBarController) using the setViewControllers method.
I have tried initializing this and simply adding it as a param in the method. There seems to be a difference between a view controller created via storyboard and one created programmatically because when I tried adding a viewcontroller:UIViewController programmatically to the setViewControllers method, it worked fine.
My code below compiles however I get a runtime exception Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7b8491598) at the line when homeTabBarController.setViewControllers is called
`
func loadTabBar() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = storyboard.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
homeViewController!.title = "Home"
homeTabBarController.setViewControllers([homeViewController!], animated: false)
homeTabBarController.modalPresentationStyle = .fullScreen
present(homeTabBarController, animated: true)
}
`
//MARK: - Create the instances of ViewControllers
let grayViewController = HomeViewController()
let blueViewController = FirstViewController()
let brownViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
//set title of the viewcontrollers
grayViewController.title = "home"
blueViewController.title = "first"
brownViewController.title = "second"
//Assigne the viewcontrollers to the TabBarViewController
self.setViewControllers([ grayViewController, blueViewController, brownViewController], animated: false)
//set system images to each tabBars
guard let items = self.tabBar.items else {
return
}
let images = ["house.fill", "star.fill", "bell.fill"]
for i in 0...2 {
items[i].image = UIImage(systemName: images[i])
}
self.tabBar.tintColor = .black
}
// You can download the project from the below github link
https://github.com/ahmetbostanciklioglu/AddingTabBarControllerProgrammatically.git
I have implemented a tabBar programmatically:
class ViewController: UIViewController {
let tabBarCnt = UITabBarController()
override func viewDidLoad() {
super.viewDidLoad()
tabBarCnt.tabBar.tintColor = UIColor.black
createTabBarController()
}
func createTabBarController() {
let firstVc = UIViewController()
let downloadViewController = DownloadsViewController()
downloadViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 0)
let bookmarkViewController = BookmarksViewController()
bookmarkViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 1)
let favoritesViewControllers = FavoritesViewController()
favoritesViewControllers.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 2)
// Adding navigationControllers
let controllerArray = [downloadViewController, bookmarkViewController, favoritesViewControllers]
// For somereason this made the word Home appear in first tab
tabBarCnt.viewControllers = controllerArray.map{ UINavigationController.init(rootViewController: $0)}
self.view.addSubview(tabBarCnt.view)
}
}
DownloadViewController.swift
class DownloadsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
self.title = "Downloads"
}
}
This is the result:
MY QUESTION IS:
Do I have to implement all the layouts of the different viewControllers downloadViewController, bookmarkViewController, favoritesViewControllers programmatically?
Can't I use the storyboard and associate a viewController and do all the UI design and implementation there like this:
The problem here is that if I will implement all the layout programmatically, it won't be an obvious or a practical thing to do.
And assigning DownloadsViewController to that storyboard ViewController, doesn't make it get display it when I navigate to DownloadsTab in the UITabBar.
Yes, you can do that currently your creating view controller instance programmatically, instead of that you have to load/get viewController instance from storyBoard like this.
UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(identifier: "downloadsVC") as! DownloadsViewController
before calling this method set viewController StoryboardID
I have a design made in nib and in round about all ViewControllers I am loading that nib design.
Now as that was common footer so I managed to used it in the footer of all other view controllers.
Now what I want : I want that whenever User click on a footer it must start a new View Controller that will show what you can say "About us " view controller.
What I am doing:
// ON CLICK OF FOOTER I AM DOING
let mAboutUs = self.storyboard?.instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
mAboutUs.modalPresentationStyle = .fullScreen
self.present(mAboutUs, animated: true) {
}
but I am getting following error
Value of type 'FooterView' has no member 'storyboard'
My Understanding: I think from nib file we can not start a new ViewController, But I really do not want to do this thing in all other View controllers in which I added this nib (FooterView) as my footer view at bottom of each view controller.
Please help me!!!!
A UIView subclass doesn't contain a storyboard property it's for a vc subclass , You need
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
if you want to present a vc from inisde the view FooterView then add a delegate like
weak var delegate:VCName?
When you create an instance
let ins = FooterView() // or from storyboard
ins.delegate = self
Then use
delegate?.present(mAboutUs, animated: true)
inside the view's class
You can use a delegate that all vcs conforms to but the easiest is to add this extension
extension UIViewController {
func topMostViewController() -> UIViewController {
if let presented = self.presentedViewController {
return presented.topMostViewController()
}
if let navigation = self as? UINavigationController {
return navigation.visibleViewController?.topMostViewController() ?? navigation
}
if let tab = self as? UITabBarController {
return tab.selectedViewController?.topMostViewController() ?? tab
}
return self
}
}
Then
guard let currentVC = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController.topMostViewController() else { return }
currentVC.present(mAboutUs, animated: true)
Your code should look similar to this:
let vc = UIStoryboard(name: "<Name of the storyboard that contains the About Us VC>", bundle: nil).instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
self.present(vc, animated: true) {
}
I want to change the default selected view controller of a UITabBar / UITabBarController via selectedIndex.
I tried other solutions I saw in other threads, like changing it in the AppDelegate or in ViewDidAppear like this:
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupTabBar()
}
override func viewDidAppear(_ animated: Bool) {
// this is the attempt to set the selected index that doesn't work
self.tabBarController?.selectedIndex = 2
}
func setupTabBar(){
let vc1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "mainvc")
let vc2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondvc")
let vc3 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "thirdvc")
let FirstController = UINavigationController(rootViewController: vc1)
let SecondController = UINavigationController(rootViewController: vc2)
let ThirdController = UINavigationController(rootViewController: vc3)
viewControllers = [FirstController, SecondController, ThirdController]
}
}
Nothing worked so far, and it always shows the selected index = 0.
I set the tab bar view controller as the initial view controller in the Main storyboard. Should I be doing this in the AppDelegate instead?
You are saying that
class MainTabBarController: UITabBarController {
so self refers to a UITabBarController, but you are setting the selected index of tabBarController of self:
self.tabBarController?.selectedIndex = 2
There are no tab bar controllers embedded in self. self itself is a tab bar controller! You can just set self.selectedIndex:
self.selectedIndex = 2
Also, you should do that in viewDidLoad instead of viewDidAppear, because viewDidAppear can happen many times (every time some modal controller is dismissed for example). viewDidLoad will only be called once.
I'm trying to present a SharkProfileTableViewController from my Main.storyboard file with a TabBarViewController from a button action in a modal view in Upload.storyboard that has no tab bar.
The ViewController I'm trying to present is a selected item from a SharksTableViewController tableview based on the data from the modal view.
How do I show the SharkProfileViewController along with the TabBarViewController?
#IBAction func viewProfileButtonPressed(_ sender: UIButton) {
let stb = UIStoryboard(name: "Main", bundle: nil)
let tabBar = stb.instantiateViewController(withIdentifier: "tabBar") as! TabBarViewController
let sharkTabBar = stb.instantiateViewController(withIdentifier: "sharkTableView") as! SharksTableViewController
let sharkProfile = stb.instantiateViewController(withIdentifier: "sharkProfile") as! SharkProfileTableViewController
sharkProfile.selectedShark = shark as JSONObject
tabBar.selectedIndex = 3
self.present(tabBar, animated: true) {
}
}
TabBarController - the 'Shark' tab is the tab that should show
SharksTableViewController - when selecting an item in this tableview it presents the ...
SharkProfileTableViewController - this is the view I'm trying to present (with the tab bar showing)
If you want to pass any data to SharkProfileTableViewController that you have added in UITabbarController then you can access it using the viewControllers property of UITabbarController. viewControllers property will return array of UIViewController so you need to access it using subscript with your controller index in Tabbar.
#IBAction func viewProfileButtonPressed(_ sender: UIButton) {
let stb = UIStoryboard(name: "Main", bundle: nil)
let tabBar = stb.instantiateViewController(withIdentifier: "tabBar") as! TabBarViewController
//If SharkProfileTableViewController at 4 position in tabbar then access it from array like this
let nav = tabBar.viewcontrollers?[3] as! UINavigationController
let sharkProfile = stb.instantiateViewController(withIdentifier: "sharkProfile") as! SharkProfileTableViewController
sharkProfile.selectedShark = shark as JSONObject
tabBar.selectedIndex = 3
self.present(tabBar, animated: true) {
nav.pushViewController(sharkProfile, animated: false)
}
}
Now you need to simply change index in viewcontrollers?[3] to access other controller from array.