UITabbarController replace the last view controller - ios

I need replace the last view controller, depending on the condition. Into TabBarController I have 4 view controllers, last view controller it's LoginVC. If I'm already logged in I need replace LoginVC to ProfileVC.
I try:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if tabBarController.selectedIndex == 3 && !DefaultsManager.instance.getUserToken().isEmpty {
showProfile()
return false
}
return true
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 3 && !DefaultsManager.instance.getUserToken().isEmpty {
showProfile()
}
}
func showProfile() {
let profile = ProfileVC.instantiateNCFromStoryboard()
self.viewControllers?[3] = profile
}
But it's does't work for me.
screen with login screen
screen profile screen with my condition

In AppDelegate check user login status and change the last view controller and its tabBarItem
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let rootVC = self.window?.rootViewController as? UITabBarController, var viewControllers = rootVC.viewControllers {
viewControllers.removeLast()
if userLoggedIn {
let profileVC = ProfileVC()
//profileVC.tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "imgName"), selectedImage: UIImage(named: "imgName"))
profileVC.tabBarItem = UITabBarItem(title: "Profile", image: nil, selectedImage: nil)
viewControllers.append(profileVC)
} else {
let loginVC = LoginVC()
//loginVC.tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "imgName"), selectedImage: UIImage(named: "imgName"))
loginVC.tabBarItem = UITabBarItem(title: "Login", image: nil, selectedImage: nil)
viewControllers.append(loginVC)
}
rootVC.viewControllers = viewControllers
}
return true
}
In LoginVC upon successfull login, remove LoginVC from tabBarController and add ProfileVC
#objc func loginBtnAction(_ sender: UIButton) {
if var viewControllers = self.tabBarController?.viewControllers {
viewControllers.removeLast()
let profileVC = ProfileVC()
//profileVC.tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "imgName"), selectedImage: UIImage(named: "imgName"))
profileVC.tabBarItem = UITabBarItem(title: "Profile", image: nil, selectedImage: nil)
viewControllers.append(profileVC)
self.tabBarController?.viewControllers = viewControllers
}
}
In Logout button action from ProfileVC, remove ProfileVC from tabBarController and add LoginVC
#objc func logoutBtnAction(_ sender: UIButton) {
if var viewControllers = self.tabBarController?.viewControllers {
viewControllers.removeLast()
let loginVC = LoginVC()
//loginVC.tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "imgName"), selectedImage: UIImage(named: "imgName"))
loginVC.tabBarItem = UITabBarItem(title: "Login", image: nil, selectedImage: nil)
viewControllers.append(loginVC)
self.tabBarController?.viewControllers = viewControllers
}
}

Related

how can implement coordinator in UITabBarController

i have a UITabBarViewController that i have created programmatically and i had imported all my 3 UIViewControllers , Now I wanna add Coordinator to my Project and i want to add Coordinator to all my 3 UIViewControllers.
how can i Use It?
class MainTabbarViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// home Tab
let homeStoryboard: UIStoryboard = UIStoryboard(name: "HomeStoryboard", bundle: nil)
let homeTab = homeStoryboard.instantiateViewController(withIdentifier: "HomeViewController")
let homeTabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house.fill"))
homeTab.tabBarItem = homeTabBarItem
// search Tab
let searchStoryboard: UIStoryboard = UIStoryboard(name: "SearchStoryboard", bundle: nil)
let searchTab = searchStoryboard.instantiateViewController(withIdentifier: "SearchViewController")
let searchTabBarItem = UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass"), selectedImage: UIImage(systemName: "magnifyingglass"))
searchTab.tabBarItem = searchTabBarItem
// profile Tab
let profileStoryboard: UIStoryboard = UIStoryboard(name: "ProfileStoryboard", bundle: nil)
let profileTab = profileStoryboard.instantiateViewController(withIdentifier: "ProfileViewController")
let profileTabBarItem = UITabBarItem(title: "Profile", image: UIImage(systemName: "person"), selectedImage: UIImage(systemName: "person.fill"))
profileTab.tabBarItem = profileTabBarItem
profileTab.navigationController?.navigationBar.isHidden = true
self.tabBar.tintColor = UIColor(asset: Asset.Colors.yellow)
self.viewControllers = [homeTab, searchTab, profileTab]
}
}
this is my UITabBarController
protocol Coordinator {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get set }
func start()
}
and this is my Coordinator protocol.
this file should remove and add an other file.
i named TabCoordinator with this Code:
final class TabCoordinator: NSObject, TabBarCoordinatorProtocol {
// Root View Controller
var rootViewController: UIViewController {
return tabController
}
// Empty UITabBarController
let tabController: UITabBarController
// Tab Item Coordinators
let homeCoordinator: HomeCoordinator
let searchCoordinator: SearchCoordinator
let profileCoordinator: ProfileCoordinator
// Initialize
override init() {
tabController = UITabBarController()
var controllers: [UIViewController] = []
// Home Coordinator
homeCoordinator = HomeCoordinator(navigationController: UINavigationController())
let homeViewController = homeCoordinator.rootViewController
homeViewController.tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house.fill"))
homeCoordinator.start()
// Search Coordinator
searchCoordinator = SearchCoordinator(navigationController: UINavigationController())
let searchViewController = searchCoordinator.rootViewController
searchViewController.tabBarItem = UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass"), selectedImage: UIImage(systemName: "magnifyingglass"))
searchCoordinator.start()
// Profile Coordinator
profileCoordinator = ProfileCoordinator(navigationController: UINavigationController())
let profileViewController = profileCoordinator.rootViewController
profileViewController.tabBarItem = UITabBarItem(title: "Profile", image: UIImage(systemName: "person"), selectedImage: UIImage(systemName: "person.fill"))
profileCoordinator.start()
super.init()
controllers.append(homeViewController)
controllers.append(searchViewController)
controllers.append(profileViewController)
tabController.viewControllers = controllers
tabController.tabBar.tintColor = UIColor(asset: Asset.Colors.yellow)
tabController.tabBar.isTranslucent = false
}
}
and TabBarCoordinatorProtocol file incloude this codes
public protocol TabBarCoordinatorProtocol {
var rootViewController: UIViewController { get }
}

Why navigation controller is nil even though I push it

I have a LoginViewController and want to push MainTabBarController
So, I do this
let controller = MainTabBarController()
navigationController?.pushViewController(controller, animated: true)
LoginViewController has a navigation controller but MainTabBarController doesn't for some reasons.
I have two questions, if my flow is LoginViewController -> MainTabBarController -> Other Controllers embedded in MainTabBarController, and I want to log out, how do I get to the LoginViewController without memory leaks? I was thinking about something like popToRootViewController but not sure if it would work.
This is how I'm creating other controllers in MainTabBarController
private func createNewViewController(viewController: UIViewController, title: String, imageName: String) -> UIViewController {
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.tabBarItem = UITabBarItem(title: title, image: UIImage(systemName: imageName), selectedImage: nil)
navigationController?.view.backgroundColor = .white
viewController.navigationItem.title = title
return viewController
}
But it doesn't really work since navigationController is nil. I don't use storyboards at all though.
EDIT: Apparently embedding a UITabBarController inside a UINavigationController is not supported.
Try this design. When LoginVC appears it checks if user is logged in and re-directs to MainVC. This is just a skelton, add more details as you go forward:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = LoginVC()
return true
}
}
class LoginVC: UIViewController {
var isUserLoggedIn = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.backgroundColor = .red
if isUserLoggedIn {
present(MainVC(), animated: true)
}
}
}
class MainVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
modalPresentationStyle = .fullScreen
let firstVC = UIViewController()
firstVC.tabBarItem = UITabBarItem(title: "firstVC", image: nil, selectedImage: nil)
let secondVC = UIViewController()
secondVC.tabBarItem = UITabBarItem(title: "secondVC", image: nil, selectedImage: nil)
viewControllers = [firstVC, secondVC]
}
}

Embed custom tab bar controller after loginVC programmatically

I am working without Storyboards.
After the successful login, I'd like to add a tabBar into my viewControllers.
I created another viewController called tabBar controller with the code:
class TabBarController: UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
// Create Tab one
let tabOne = Home()
let tabOneBarItem = UITabBarItem(title: "Collection", image: #imageLiteral(resourceName: "matchTabIcon"), selectedImage: #imageLiteral(resourceName: "matchTabIconSelected"))
tabOne.tabBarItem = tabOneBarItem
// Create Tab two
let tabTwo = ScoutingVC()
let tabTwoBarItem2 = UITabBarItem(title: "Scouting", image: #imageLiteral(resourceName: "scouting"), selectedImage:#imageLiteral(resourceName: "scoutingSelected"))
tabTwo.tabBarItem = tabTwoBarItem2
self.viewControllers = [tabOne, tabTwo]
}
// UITabBarControllerDelegate method
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("Selected \(viewController.title!)")
}
}
What is the correct way to add this to all of my VCs?
I tried
self.vc.addSubView(tabBarController)
and also to create a func() in the first VC (index: 0), but either the tabBar is not there, or if there, doesn't switch between viewControllers.
func showTabBarController() {
// Create Tab one
let home = Home()
let homeTabBarItem = UITabBarItem(title: "Collection", image: #imageLiteral(resourceName: "matchTabIcon"), selectedImage: #imageLiteral(resourceName: "matchTabIconSelected"))
home.tabBarItem = homeTabBarItem
let navHome = UINavigationController.init(rootViewController: home)
// Create Tab two
let scouting = ScoutingVC()
let scoutingTabBarItem = UITabBarItem(title: "Scouting", image: #imageLiteral(resourceName: "scouting"), selectedImage: #imageLiteral(resourceName: "scoutingSelected"))
scouting.tabBarItem = scoutingTabBarItem
let navScouting = UINavigationController.init(rootViewController: scouting)
//showTabBar
tabBarCnt.viewControllers = [navHome, navScouting]
self.view.addSubview(tabBarCnt.tabBar)
}
Instead of using self.vc.addSubView(tabBarController) use present(TabBarController(), animated: false, completion: nil)

swift 3 keep bottom tabbar on children windows?

i am trying to keep bottom tab bar in children viewcontroller , my issue when i open children window its open without bottom tabs , how i can keep bottom tabs stuck everywhere in the app ?
this is the class of main tabs window ( landing )
class vc_landingPage: UITabBarController , UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//Delegate methods
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
print("Should select viewController: \(String(describing: viewController.title)) ?")
return true;
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let sb2 = UIStoryboard(name: "pools", bundle: nil)
let v1 = sb2.b_pools.instantiateInitialViewController()!
v1.tabBarItem = UITabBarItem( title : "" , image: UIImage(named: "icon-pools-x30"), selectedImage: UIImage(named: "icon-pools-x30-active"))
let sb = UIStoryboard(name: "myProfile", bundle: nil)
let v2 = sb.instantiateInitialViewController()!
v2.tabBarItem = UITabBarItem( title : "" , image: UIImage(named: "icon-profile-x30"), selectedImage: UIImage(named: "icon-profile-x30-active"))
self.viewControllers = [v1,v2]
self.selectedIndex = 1
}
}
Please you must manage the children window in a UINavigationController.
let v1 = sb2.b_pools.instantiateInitialViewController()!
let navi1 = UINavigationController.init(rootViewController: v1)
let v2 = sb.instantiateInitialViewController()!
let navi2 = UINavigationController.init(rootViewController: v2)
self.viewControllers = [navi1,navi2]
Make sure your BOTTOM BAR is not none, make sure it is not set to any of your view controller or your tab bar controller

UITabBar Controller Present ViewController of Second tab Without Segue

I'm eliminating the storyboard from my app completely. How do I present the VC that is linked to the second tab of the TabBarController.
Setup: mainVC --- myTabBar -- tab1 - navCntrl - VC1
tab2 - navCntrl - VC2
When using a segues I used the following code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == myTabBar) {
let tabVC = segue.destination as? UITabBarController {
tabVC.selectedIndex = myTabBarIndex ==> 1 to reach VC2
}
// other other stuff
}
To eliminating the segues I rewrote the above but although I set the selectedIndex VC2 is not presented. Any suggestions?
func vc2Btn() {
let tabVC = MyTabBar()
tabVC.selectedIndex = 1 // ==>> Index set but can not reach VC2
present(tabVC, animated: true, completion: nil)
}
The full code of my test system:
class MyTabBar: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create Tab 1
let navCtrlTab1 = UINavigationController(rootViewController: VC1())
let tabOne = navCtrlTab1
let tabOneBarItem = UITabBarItem(title: "", image: StyleKit.imageOfIconTabRecent, selectedImage: StyleKit.imageOfIconTabRecentRev)
tabOne.tabBarItem = tabOneBarItem
// Create Tab 2
let navCtrlTab2 = UINavigationController(rootViewController: VC2())
let tabTwo = navCtrlTab2
let tabTwoBarItem = UITabBarItem(title: "", image: StyleKit.imageOfIconTabNote, selectedImage: StyleKit.imageOfIconTabNoteRev)
tabTwo.tabBarItem = tabTwoBarItem
self.viewControllers = [tabOne, tabTwo]
}
}
class mainVC: UIViewController {
let btn0: UIButton = {
let button = UIButton()
button.setBackgroundImage(StyleKit.imageOfBtnBlue(btnText: "VC1"), for: UIControlState.normal)
button.addTarget(self, action:#selector(vc1Btn), for: .touchUpInside)
return button
}()
let btn1: UIButton = {
let button = UIButton()
button.setBackgroundImage(StyleKit.imageOfBtnBlue(btnText: "VC2"), for: UIControlState.normal)
button.addTarget(self, action:#selector(vc2Btn), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(btn0)
self.view.addSubview(btn1)
addConstraintsWithFormat("H:|-100-[v0]", views: btn0)
addConstraintsWithFormat("H:|-100-[v0]", views: btn1)
addConstraintsWithFormat("V:|-300-[v0]-20-[v1]", views: btn0, btn1)
}
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
func vc1Btn() {
let tabVC = MyTabBar()
tabVC.selectedIndex = 0 // ==>> this is working
present(tabVC, animated: true, completion: nil)
}
func vc2Btn() {
let tabVC = MyTabBar()
tabVC.selectedIndex = 1 // ==>> Index set but can not reach VC2
present(tabVC, animated: true, completion: nil)
}
}
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "VC1"
print ("VC1")
}
}
class VC2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "VC2"
print ("VC2")
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = mainVC()
return true
}
}

Resources