Selected Index on Tab Bar not working on Swift - ios

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.

Related

Skip login screen from scene delegate in tabbed bar navigation App

I am trying to skip the login screen and go straight to the MainViewController when the user is logged in. However the problem is that I have a Tab Bar Controller and Navigation Controller between the login and the main vc. After extensive search I wrote the below code
func showMainViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController: MainViewController = storyboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
let navigationController = UINavigationController(rootViewController: mainViewController)
//It removes all view controllers from the navigation controller then sets the new root view controller and it pops.
window?.rootViewController = navigationController
// //Navigation bar is hidden
// navigationController.isNavigationBarHidden = true
}
However it fails to show the tab bar view controller. Any help is appreciated.
try this
func showMainViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController: MainViewController = storyboard.instantiateViewController(withIdentifier: "TabBar")
window?.rootViewController = mainViewController
window?.makeKeyAndVisible()
}
you should instantiate TabBar no main viewController. since is instantiate inmediatrly is if first index or set the selected index

How to invoke Navigation controller form a VC

I have 3 VC's (VC as in ViewController)
1 AuthViewController which is the entry point for the app and it checks if user is logged in else it displays the sign-in related views
2 HomeViewController is the main interface of the app landing page after the user has logged in
3 ListViewController which displays a list, invoked via a segue on HomeViewController.
I want to enable back navigation between ListViewController and HomeViewController via a Navigation Controller.
How can I achieve this If I have a Navigation controller whose root VC is HomeVC. how can I invoke it from my AuthVC so that it gets invoked with the Navigation controller.
I have tried to invoke the Navigation controller but did not work
I have also tried invoking the HomeVC by
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "mainViewController") as? UIViewController {
viewController.modalPresentationStyle = .overCurrentContext
self.present(viewController, animated: true, completion: nil)
although I was able to invoke the HomeVC could not get the navigation buttons
How can I invoke HomeVC from the AuthVC without loosing the NavigationController
You need 1 navigation controller, not 3.
If you want to push a new controller, then use push method, not present.
The back button gets enabled automatically on the navigation bar if there are controllers in the navigation stack and the current controller is not a root view controller.
When coming from AuthViewController to HomeController, you should change the RootViewController. So that the user cant go back to the Auth screen. That would make no sense.
Once you are on HomeController, you can push other controller using the below code and it will add a back button to your controller automatically.
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "mainViewController") as? UIViewController {
self.navigationController?.pushViewController(vc, animated: true)
}
You can also use the below extension:
extension UIViewController {
func pushVC(storyboardName : String, vcname : String) {
let vc = UIStoryboard.init(name: storyboardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
func popVC() {
self.navigationController?.popViewController(animated: true)
}
func makeRootVC(storyBoardName : String, vcName : String) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = UIStoryboard(name: storyBoardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
let nav = UINavigationController(rootViewController: vc)
nav.navigationBar.isHidden = true
appDelegate.window?.rootViewController = nav // If using XCode 11 and above, copy var window : UIWindow? in your appDelegate file
let options: UIView.AnimationOptions = .transitionCrossDissolve
let duration: TimeInterval = 0.6
UIView.transition(with: appDelegate.window!, duration: duration, options: options, animations: {}, completion: nil)
}
}

ios swift - instantiating same VC from multiple tabs

I have multiple tabs connected via separate navigation controllers. I want to to instantiate same End Pt VC, irrespective of which tab user chooses tabs 1 through 4.
When I select Tab 1 it shows End Pt VC because it is connected via Segue. However when I select other tabs I manually try to push EndPt VC as shown in the tab controller method. But it shows blank screen. How can I present the same End Pt VC irrespective of tab selection?
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "EndPointTVC") as? EndPtListTableViewController{
viewController.selectedTab = self.selectedTab
if let navigator = self.navigationController {
navigator.pushViewController(viewController, animated: true)
}
}
}
You can detect when the UITabBarController changes tab via its delegate UITabBarControllerDelegate. In the following delegate method, you can cast the selected view controller to a UINavigationController (which is what I can make out in the screenshot you attached).
extension MyTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let myVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyViewControllerStoryID") as! MyViewController
let navController = viewController as! UINavigationController
navController.viewControllers = [myVC]
print(viewController)
}
}
Now, no matter which tab you choose, a new MyViewController instance is created. Even hitting the same tab multiple times will replace the current instance for a new MyViewController instance.
Try to instantiate the viewcontroller from the storyboard and then present it. Also, make sure that you have given the target viewcontroller the storyboard Id "EndPointTVC" in the Identify section.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier : "EndPointTVC")
viewController.selectedTab = self.selectedTab
self.present(viewController, animated: true)

How can I show ViewController in UITabBarController?

I have a UITabBarController and all my other view controllers are connected to it. Now I want to show one my controller as:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
but when I tried to:
let rootViewController = self.window?.rootViewController as! UINavigationController
rootViewController.pushViewController(vc, animated: true)
it gave me the next error:
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Later I've tried to do:
let rootViewController = self.window?.rootViewController as! UITabBarController
but in this case I get
UITabBar has no member pushViewController
How can I show/push my ViewController so it will appear with UINavigationBar and inside of UITabBar?
You need to place each of your view controllers inside a navigation controller.
E.g. currently you have a TabBarViewController
and two view controllers:
ViewControllerA
ViewControllerB
What you need to do is to embed each of them inside a navigation controller so you would have:
UINavigationController -> ViewControllerA
UINavigationController -> ViewControllerB
In order to push a new controller you would do:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
let navViewController = myTabBar.selectedViewController as? UINavigationController
navViewController?.pushViewController(vc, animated: true)
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Your root view controller is of type UITabBarController.
Therefore you have to use the appropriate methods of this class and not UINavigationController.
To set view controllers use either
var viewControllers: [UIViewController]? or
func setViewControllers([UIViewController]?, animated: Bool).
To have a navigation bar you have to instantiate a UINavigationController and add your view controller to this navigation controller.
Then add your navigation controller to your UITabBarController with via one of the above options.
If your class is UITabBarController
You can add this:
private func showViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "queueTableViewController") as? QueueTableViewController else { return }
let navVC = self.selectedViewController as? UINavigationController
navVC?.pushViewController(vc, animated: true)
}
This will allow you to go to any VC
The second solution is something like this.
Here You will show the last VC and from there push some VC.
guard let tabCount = viewControllers?.count, tabCount > 1 else { return }
selectedIndex = tabCount - 1
if let navigationController = viewControllers?[selectedIndex] as? UINavigationController {
if let accountVC = navigationController.visibleViewController as? FirstViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "someViewController") as? SomeViewController else { return }
accountVC.navigationController?.pushViewController(vc, animated: true)
}
}

How to instantiate a navigation controller from another view controller?

My goal is whenever i click a button on a first view controller, then it will navigate to another controller which is a navigation controller.
firstViewController and secondViewController has no connection or anything.
Picture
I used this code
#IBAction func buttonTapped(sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("secondViewCtrl") as! SecondViewController
self.presentViewController(vc, animated: true, completion: nil)
}
The reason why i instatiate so that I could pass data like
vc.name = "Myname"
The problem with this code is that it doesn't present navigation bar and as well as the tab bar. What should I do to show both?
Updated question
#IBAction func buttonTapped(sender: UIButton) {
guard let tabBarController = tabBarController else { return }
tabBarController.selectedIndex = 1
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("trackYourGenie") as! TrackYourGenieViewController
let navController = tabBarController.viewControllers![1]
let secondViewController = navController.topViewController
vc.name = "Myname"
}
You are instantiating the view controller hence you wouldn't get the navigation bar. To get the navigation bar please instantiate navigation controller and since the second view is only child you would get the second view by default.
#IBAction func buttonTapped(sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("Navigation Controller Id") as! UINavigationController
self.presentViewController(vc, animated: true, completion: nil)
}
The above code should give you the navigation bar.
A safe approach:
guard let tabBarController = tabBarController else { return }
tabBarController.selectedIndex = 1
If you need to access to your tabBarController to pass datas you can do simply:
let navController = tabBarController.viewControllers[1]! as! UINavigationController
let secondViewController = navController.topViewController
Your method could be:
#IBAction func buttonTapped(sender: UIButton) {
guard let tabBarController = tabBarController else { return }
let navController = tabBarController.viewControllers[1]! as! UINavigationController
let secondViewController = navController.topViewController as! SecondViewController
secondViewController.name = "my name"
tabBarController.selectedIndex = 1
}
In your case, the following code should be sufficient :
self.tabBarController.selectedIndex = 1
As UITabBarController is your rootViewController, you can access it with self.tabBarController. You don't have to instantiate UINavigationController as it is in the storyboard.
I can see from your StoryBoard that you have a TabBarController. If your configuration is that and FirstViewController is on first tab and SecondViewController on second tab, you can just change TabBarController selectedIndex property:
#IBAction func buttonTapped(sender: UIButton) {
tabBarController?.selectedIndex = 1
}
If you want to pass data to SecondViewController you can try one of these solutions:
let controller = tabBarController.viewControllers[1] as SecondViewController!
controller.data = "some data"
This soultion could not work since SecondViewController is not ready yet.
Create a singleton class where to save data, then retrieve data in SecondViewController viewDidLoad method
Save data in UserDefaults, then retrieve data in SecondViewController viewDidLoad method (bad solution if information doesn't have to be persistent)
Extend UITabBarController and use it, create a custom var in tabBarController, put data in that variable and then retrieve data in SecondViewController viewDidLoad method
Then clear data if needed.
Navigation will not work from firstViewController as to navigate something we need UINavigationController.
Try Like This

Resources