Embed a view controller inside tab bar programatically - ios

I want to dynamically load different view controllers on click of tab bar item. But screen appears black not able to see the view controller :(
Any help is appreciated. Here is the logic that i am using:
class TabBarController : UITabBarController,ENSideMenuDelegate{
override func viewDidLoad() {
print("Inside Tab bar controller")
tabBar.items?[0].tag = 0
tabBar.items?[1].tag = 1
tabBar.items?[2].tag = 2
tabBar.items?[3].tag = 3
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
//print("Selected Index :\(self.selectedViewController) \(self.selectedIndex)");
print("Selected Item is \(item.title) --> \(item.tag)")
if( item.tag == 2 ) {
print("Navigate to ratings view for service provider ")
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
let destViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewX")
//tabBarController?.viewControllers = [destViewController]
tabBarController?.setViewControllers([destViewController], animated: true)
}
}
}

Related

How to make Custom TabBar?

I have to customize the tab bar like the picture above. However, I don't know how to customize this point because the name of the item should appear only when the tab bar is selected, and the name of the item should not appear if it is not selected. Please help me.
As an example, it was written with two items.
You can branch to the tag depending on the selected item in didSelect() method.
In viewWillAppear(), I wrote the title of first item because the first item is selected when the app is first launched. (initialization)
I hope my answer is helpful to you.
TabBarController.swift
import UIKit
class TabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
//Setting the UITabBarItem
let tab1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ViewController")
let tab1BarItem = UITabBarItem(title: "home", image: UIImage(systemName: "seal"), selectedImage: UIImage(systemName: "seal.fill"))
tab1.tabBarItem = tab1BarItem
tab1.tabBarItem.tag = 0
let tab2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "SearchViewController")
let tab2BarItem = UITabBarItem(title: "", image: UIImage(systemName: "checkmark.seal"), selectedImage: UIImage(systemName: "checkmark.seal.fill"))
tab2.tabBarItem = tab2BarItem
tab2.tabBarItem.tag = 1
self.viewControllers = [tab1, tab2]
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 0 { // tab1(home)
item.title = "home"
tabBar.items?[1].title = ""
}
if item.tag == 1 { // tab2(search)
item.title = "search"
tabBar.items?[0].title = ""
}
}
}
Preview
You need to do 2 things
1- To make the the selected tab color green
tabController.tabBar.tintColor = UIColor.green
2- When item is selected listen for UITabBarControllerDelegate and assign items with text set to "" for every unselected item
tabController.tabBar.items = [] // set all items

How to show different ViewControllers when tapping on tabBar in swift

I have three viewControllers and two of them connected to a tabBarController. So app should show one of the two vc’s depending Bool value when tapping on tabBar item here is my storyboard
import UIKit
class TabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let userLoggedIn: Bool!
if tabBarController.selectedIndex == 1{
if userLoggedIn == true{
// show firstVC
}else{
// show secondVC
}
}
}
}
You can use childViewController with the parent is ViewController fromStoryboard.
override func viewDidLoad() {
super.viewDidLoad()
///add two ChildVC here, hide 1 ifneeded
}
When click to this tab you check userLoggedIn.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if userLoggedIn == true {
/// show first childVC
/// hide second childVC
} else {
/// hide first childVC
/// show second childVC
}
}
You can check demo: Demo
Simply add .overCurrentContext to modalPresentationStyle in your viewDidLoad like this.
let newVC = self.storyboard?.instantiateViewController(withIdentifier:"secondVC")
newVC?.modalPresentationStyle = .overCurrentContext
self.present(newVC!, animated: false, completion: nil)
Simply set the viewController list when the userLoggedIn variable is modified (assuming userLoggedIn is TabBarCOntroller instance variable) :
class TabBarC: UITabBarController {
#IBOutlet var firstVC : FirstViewController!
#IBOutlet var secondVC : SecondViewController!
#IBOutlet var thirdVC : ThirdViewController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
for vc in self.viewControllers! {
if let fv = vc as? FirstViewController {
firstVC = fv
} else if let fv = vc as? SecondViewController {
secondVC = fv
} else if let fv = vc as? ThirdViewController {
thirdVC = fv
}
}
}
var userLoggedIn : Bool = false {
didSet {
if userLoggedIn {
self.viewControllers = [firstVC, thirdVC]
} else {
self.viewControllers = [secondVC, thirdVC]
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self. userLoggedIn = false
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 3 { // For purpose of test i set tag for 3 VC to 1 for firsttVC, 2 for secondVC, 3 for thirdVC
// for test : change login value each time thirdVC is selected
userLoggedIn = ! userLoggedIn
}
}
So each time from anywhere in your code you setup userLoggedIn, the tabor show the wanted tab bar items.
The 3 VC are added to the tabbar in the stpryboard.
When selecting thirdVC, the first tab bar item changes between one and two

How can I bring a collectionView back to the top when the tabBar icon for that respective view controller is pressed?

I am trying to build a social media app in swift that has a tab bar with 4 icons each connected to a view controller. I want to have the four collection views (that are each associated with a view controller) scroll back to the top when the respective tab bar icon is pressed (Exactly like twitter).
I have the logic for how I want the collection view to come back to the top only if the past tab bar icon was for that respective collection view (this is so that it doesn't just automatically scroll to the top when clicking on the respective tab but needs to be pressed again) I tried to debug my first switch case but have had no luck so I do not have the full code for the other cases yet. What I try to do is reference the correct storyboard, then the correct view controller (in this case homeViewController), and finally call that respective collection view and use setContentOffset to bring it to the top.
Below I provide my whole tabBarController class.
import Foundation
import UIKit
class MainTabController: UITabBarController, UITabBarControllerDelegate {
var pastTabBar: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let tabBarIndex = tabBar.items?.index(of: item)
if tabBarIndex == pastTabBar {
switch tabBarIndex {
case 0:
print ("go to top of home")
//updateHomeCollection = 1
let HomeSB : UIStoryboard = UIStoryboard(name: "Home", bundle: nil)
let HomeVC = HomeSB.instantiateViewController(withIdentifier: "Home") as? HomeViewController
HomeVC?.collectionView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
case 1:
print ("go to top of search")
case 2:
print ("go to top of notifications")
case 3:
print ("go to top of profile")
default:
print ("not working")
}
}
if let tabBarIndex = tabBarIndex {
pastTabBar = tabBarIndex
}
}
}
It keeps signal aborting when I click the home tab! It is printing this exact statement:
Thread 1: Fatal error: Unexpectedly found nil while implicitly
unwrapping an Optional value
on this line of code:
HomeVC?.collectionView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
Loading ViewController is not best way. You have to fetch your desired ViewController from Navigation like below
extension HomeTabVC: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if previousController == viewController {
if let nav = viewController as? UINavigationController, let vc = nav.viewControllers[0] as? FeaturedVC {
if vc.isViewLoaded && (vc.view.window != nil) {
let visibleIndex = vc.collectionFeatured.indexPathsForVisibleItems
if visibleIndex.count != 0 {
vc.collectionFeatured.scrollToItem(at: IndexPath (item: 0, section: 0), at: .bottom, animated: true)
}
}
}else if let nav = viewController as? UINavigationController, let vc = nav.viewControllers[0] as? CategoryVC {
if vc.isViewLoaded && (vc.view.window != nil) {
let visibleIndex = vc.collectionViewCategory.indexPathsForVisibleItems
if visibleIndex.count != 0 {
vc.collectionViewCategory.scrollToItem(at: IndexPath (item: 0, section: 0), at: .bottom, animated: true)
}
}
}else{
}
}
previousController = viewController
}
}
You have to store Selected ViewController in variable so when you change ViewController from one tab to another it will just change ViewController and when you tap same tab again it will scroll you back to top. This is how iOS is doing it self in all there application

Side bar menu icon is invisible in Tab bar based view controller

I've implemented side bar using SWRevealViewController. But when i do same using Tab bar based view controller, menu icon is invisible only in tab based screens.
Icon is visible in interface builder but invisible in simulator/device.
Functionality is working in every screen, even in tab based view controller.
When i drag from left to right and vice versa, functionality is working fine.
Only the issue is menu icon is invisible in simulator.
Can anyone help me?
Thanks in advance.
below is the swift file for tab
import UIKit
class ItemOneViewController: UIViewController {
#IBOutlet weak var menuBar: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
setMenuBarBtn(menuBar: menuBar)
navigationController!.navigationBar.barTintColor = UIColor.color(.blue)
}
func setMenuBarBtn(menuBar: UIBarButtonItem) {
menuBar.target = revealViewController()
menuBar.action = #selector(SWRevealViewController.revealToggle(_:))
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
}
Code is similar for three tabs.
I've done below tasks before asking question here
Deleted and recreated screens two times.
Cross checked everything and compared tab bar based scenes source code with other scenes.
googled it.
Below are the images for your reference
Tab based screen with side bar functionality (working good)
Tab based screen without menu icon (Problem persist here , i want menu icon to be displayed)
HomeScreen with menu icon (Working good)
Navigation Controller attribute inspector
my storyboard
Item One View Controller view hierarchy
Item One view Controller related navigation controller scene
You gave to add normal button on HomeViewController
Then directly perform segue through storyboard(from burger menu button).
#IBAction func sideMenuBtnAction(_ sender: UIButton) {
performSegue(withIdentifier: "sideMenu", sender: self)
}
Then on SideMenuController :
import UIKit
import SideMenu
class SideMenuViewController: UIViewController {
#IBOutlet var sideMenuTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
SideMenuManager.default.menuPresentMode = .menuSlideIn
SideMenuManager.default.menuFadeStatusBar = false
SideMenuManager.default.menuWidth = self.view.frame.width / 1.4
SideMenuManager.default.menuShadowOpacity = 0.3
sideMenuTableView.tableFooterView = UIView(frame: .zero)
}
}
You need to create a tabbarController class like below, and assign this ViewController class to tabbar on storyboard ...
I have not tested this code but something like this should work for your case
class ViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
self.viewControllers = [firstSw,secondsw, thirdSw]
// Do any additional setup after loading the view.
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
OR Following by doing this there is no need to create a tabbarController
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// create UIWindow with the same size as main screen
window = UIWindow(frame: UIScreen.mainScreen().bounds)
// create story board. Default story board will be named as Main.storyboard in your project.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// create view controllers from storyboard
// Make sure you set Storyboard ID for both the viewcontrollers in
// Interface Builder -> Identitiy Inspector -> Storyboard ID
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
// Set up the Tab Bar Controller to have two tabs
let tabBarController = UITabBarController()
tabBarController.viewControllers = [firstSw,secondsw, thirdSw]
// Make the Tab Bar Controller the root view controller
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
return true
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}

Navigation controller isn't working as expected

I am using navigation controller for connecting 8 form page to each other. According to users choice it is navigating next screen using following code :
if (GlobalVariable.page[0] == true || GlobalVariable.page[1] == true || GlobalVariable.page[2] == true || GlobalVariable.page[3] == true) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationVC:UIViewController!
destinationVC = storyboard.instantiateViewController(withIdentifier: "firstInfo") as! FirstInfoVC
navigationController?.show(destinationVC, sender: self)
}
When I use above code user can go previous screen using swipe left gesture. It is okay until now. However, every page has menu button and after filled 8 page or whenever they wants user can click that menu button and according to choice it needs to navigate the another view controller independent from that 8 page. For navigating that independent view controller I am also trying to use above code in my menu table view didselectrowat function but it is not working (actually, it is doing nothing). What could be the reason of it and how can I fix it?
Edit :
In my side navigation controller class
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let menu = storyboard.instantiateViewController(withIdentifier: "MenuTableViewController") as! MenuTableViewController
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menu, menuPosition: .left)
sideMenu?.menuWidth = UIScreen.main.bounds.width * 0.8
sideMenu?.bouncingEnabled = false
//Navigation Bar Özelleştirmesi İçin Renk Mavi Üstüne Beyaz
UINavigationBar.appearance().barTintColor = UIColor(red:0.01, green:0.47, blue:0.74, alpha:1.0)
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
view.bringSubview(toFront: navigationBar)
}
and in my menu table view code is following:
class MenuTableViewController: UITableViewController {
override func viewDidLoad()
{
super.viewDidLoad()
tableView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationVC:UIViewController!
if (indexPath.row == 2) {
destinationVC = storyboard.instantiateViewController(withIdentifier: "independentPage") as? IndependentVC
navigationController?.show(destinationVC, sender: self)
}
}
}

Resources