I have been using the tabBarController selectedIndex to determine whether a navigation bar should be displayed. Everything was working fine for most of today, but now this line of code:
self.tabBarController.selectedIndex
is giving the last selected index rather than the currently selected index. I am running this line of code in viewWillAppear to make sure that a particular view which has index 1 in the tabBarController will not show its navigation bar on its first view.
But it's reporting 1 click behind. For example, when I click on the 2nd index and then the 1st index, my last click shows up as having selected the view controller at index 2 even though I have just clicked on the view controller with index 1. Also, the selectedIndex is always listed correctly the first time I click on any view controller but never correct after the first time it is viewed via the tabBarController.
I've looked over the tabBarController class reference, but it seems like this selectedIndex property should be straightforward. What am I missing?
The problem you have is that viewWillAppear is called before the index has actually changed.
First, tabBarController shouldSelectViewController -> viewWillAppear -> tabBarController didSelectViewController -> viewDidAppear.
I have a similar setup where I reuse ViewControllers across different tabBar indices. I did not want to check for the selectedIndex in the viewDidAppear even though that would fix the problem.
What I did to fix the problem was to go by a tag. Both ViewControllers have different UINavigationController and I set the tag of one navBar to 1. Every time I need to know which Controller is active I now check for the tag.
Related
There are 5 tabs and the first 2 of those need to open the same VC which would be embedded in a navigation controller. All of the VCs are in the storyboard. Depending on which of those 2 is tapped, I need to pass a value (I think I can figure this out) which would change some of the UI components of that VC.
What I have so far is this:
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let indexOfTab = tabBar.items?.firstIndex(of: item)
switch indexOfTab {
case 1:
// Open 1st VC with 'x' UI components
case 2:
// Open 1st VC with 'y' UI components
}
So I am able to open tabs 3 to 5 without any problems but the first 2 do not work properly because they are embedded in a navigation controller.
Expected result: Tapping tab index 1 followed by tab index 2 (or the other way around) should open the 2nd VC (or 1st as the case maybe) as the root view controller
Actual result: Tapping index 1 followed by tab index 2 (or the other way around) opens tab index 1 as the root view controller and pushes tab index 2.
How do I get the expected result?
EDIT:
I think I know why this is happening. It is because I have segue to VC for those 2 VCs in the storyboard and then programatically I am trying to open them as well. Is there anyway to do this in a mix of storyboard and programmatic or do I need to refactor the entire tabbar programmatically?
You can create two link from the tabBarController to the VC you want present and assign to them an identifier, so in the prepareForSegue methods you can pass to the VC the value you want, this works if the two tabs present two different instance.
Instead if the two tab present the same instance, you can save the instance of the VC in the prepareForSegueAction or set the child VC as delegate of the parent (I prefer the second option personally) and then you can use the method to know when a tab has been pressed and pass to the VC the value you need using the instance o the method.
In the interface builder I have a UITabBarController and it is set as the initial view controller. From the tab bar controller, I have linked three independent ViewControllers; two UIViewController's and one UITableViewController. I have embedded all three of these views inside UINavigationController's as each of these views will eventually segue to a new view.
Interface Builder
Problem:
I now want to link one of the UIViewController's to the UITableViewController using a button to segue to the table view. This way I can pass information, i.e. func prepareForSegue(), to the table view. I want to maintain the tab bar controller at the bottom, however I do not want to have the ability to go back to the previous UIViewController from the current UITableViewController via a UIBarButtonItem at the the top of the view; That is what the tab bar at the bottom is for.
However every time I segue "Show" the table view (it is actually a segue to the table views navigation controller), the navigation bars at the top and the bottom of the table view disappear. Is there anyway to prevent this from happening?
I have also tried segue "Show" directly to the table view, in which case the tab bar is visible, but then it displays a "back" button at the top of the view to segue back to the sending UIViewController. I am hesitant about accepting a solution that would just hide the back button, because I feel I will run into problems down the road when I want to navigate to a detail view from the table view itself, since I would be bypassing the UITableViewController's UINavigationController.
Any solutions would be greatly appreciated. I have been trying to solve this problem for hours and I'm about to put my head through my computer screen. Also I thought about just using tabBarController?.selectedIndex on the button click to shift to the table view, and then passing the information using NSUserDefaults, but this is out of the question since I would be passing a custom object, and would have to encode and decode every custom field.
I you use a segue to get to it, as you say, you will still be using the UIViewController's UINavigationController which seems a bit messy. So I actually think selectedIndex is probably the best way to go as once you change to the UITableViewController you'll be in the correct navigation stack.
Instead of using NSUserDefaults, why not just reference the UITableView itself from the UIViewController, set the values you want, and then swap to it using self.tabBarController.selectedIndex.
So for your scenario above it, assuming the UITableViewContollrer is the third view in the UITabBarController, you would do something like the following:
Pass whatever you want into the UITabBarController by setting some pre-defined var in it. For example, if there was a String called saveMe in the UITableViewController, then do the following in the UIViewController:
let navController = self.tabBarController?.viewControllers![2] as! UINavigationController
let tableViewController = navController.viewControllers.first as! JarListTableViewController
tableViewController.saveMe = "Saved string here"
Swap to the UITableViewController using:
self.tabBarController?.selectedIndex = 2
The only issue with this is using selectedIndex won't perform a transition animation but not sure if you need this. This link could help if you do.
I have an iOS application with a Tab Bar, and two subview. My first view is a Table View.
So, I want to switch to the second view when I click on a cell of the first view, and keep the TabBar visible.
When I do that using "Show" segue in the storyboard, I lost the TabBar.
And when I do it in my TableViewController with the following code, the second view is not loading.
tabBarController.selectedViewController = mySecondViewController
That only select the second element in the TabBar, but didn't display him.
Anyone have a solution ?
try using index of UITabBar item to switch, like this:
tabBarController.selectedIndex = 1
The problem also may be because you try to set View to selectedViewController, where uiviewcontroller should be. It is hard to say, because of lack of information.
In the story board make sure you control drag from the tab bar controller to your view controller and click "Relationship Segue: View Controller"
I've fix my problem. I've originally put the line of code bellow on the viewDidLoad() of my controller :
tabBarController.selectedViewController = mySecondViewController
After moving this line in a different methode, call by a simple button, that worked...
I have a UITabBarController with 4 tabs, the first two being "Program" and "Workout".
Whilst on the first tab's view, the user can select the program they wish to perform, and then tap "Perform Workout" which will take them to the workout screen.
My aim is to show the workout screen as part of the same navigation controller as the program select, however I want to update the UITabBarController to reflect the fact that we're now on the workout screen.
The problem is that if I call
UITabBarController *tabController = (UITabBarController*)self.navigationController.parentViewController;
[tabController setSelectedIndex:1];
This opens the WorkoutViewController inside a new UINavigationController, instead of using the old one. I realise that the two UINavigationController problem is happening because of my storyboard, but if I don't have two separate UINavigationControllers, the TabView doesn't stay on top all the time (it disappears after the first push).
How can I update the selectedIndex on my UITabBarController WITHOUT forcing it to load the new UINavigationController?
I think you should disconnect your Workout controller and push this controller using storybord id not from Program controller but change the selected index of tab push this controller to second navigation cotroller
I have two tabs in the application.
From tab 2 on a particular view i want to switch to Root View of the 1st tab.
how do i do this?
In this case simply self.tabBarController.selectedIndex = 0; wont work as it does not shows root view it shows the view to which user has navigated to before leaving that tab.
I wish to directly switch to first view of the tab 1
I'm guessing that tab1 is a UINavigationController. You will need to popToRootViewController on the first tab
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
Depending on how your application is configured, you might create a method on your appdelegate or somewhere in your view hierarchy that hosts both the UITabBarController and the UINavigationController.
If you always want to display the first view of a particular tab when the user returns to it, call the popToRootView from a viewDidDissapear method somewhere. There are lots of ways to do it, but think about the user experience if you do it automatically.