So I have a UITabBarController with two View Controllers Embedded into it. I implemented the did select tab bar method where when the user selects a tab, it passes a value into that controller. However when the tabBarController loads for the first time, the did select method is not called even though I have
self.selectedIndex = 0
Which selects the first index. Basically I am just trying to automatically select the first tab Bar Item when the view loads, and have it call the didSelectTabBarItem method
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
}
The question is similar to this one.
tabBarController didSelect does not get called
optional func tabBarController(_ tabBarController: UITabBarController,
didSelect viewController: UIViewController)
In iOS v3.0 and later, the tab bar controller calls this method regardless of whether the selected view controller changed. In addition, it is called only in response to user taps in the tab bar and is not called when your code changes the tab bar contents programmatically.
Copy this in your code
class HomeTabBarVC: UITabBarController {
var isIpad = false
let button = UIButton.init(type: .custom)
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
APPDELEGATE.tabBar = self
}
}
extension HomeTabBarVC : UITabBarControllerDelegate {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("the last selected index is : \(selectedIndex)")
APPDELEGATE.tabBarLastSelectedIndex = selectedIndex
print("the current selected index is : \(String(describing: tabBar.items?.index(of: item)))")
APPDELEGATE.tabBarCurrentSelectedIndex = tabBar.items?.index(of: item) ?? 0
}
}
In appdelegate declare this variable,
var tabBar : UITabBarController?
Related
I have a Swift application with 4 bottom tabs. In the home tab I have a video running. In the 4th tab I have a favorites list.
When the user changes tabs, if he's on the home screen, the video should stop. Also, when the user taps on the 4th tab, the favorites list should update so that the user can see the recent additions.
I have the following on the home tab view controller:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("TAB CHANGED")
if let jwp = jwPlayer {
jwp.stop()
}
}
and this on the favorites tab view controller:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 3 {
let userId = UserDefaults.standard.integer(forKey: "userId")
favoritesVC.updateData(with: userId)
}
}
Both conform to UITabBarControllerDelegate. Both include:
self.tabBarController?.delegate = self
When I launch the app, the video starts playing, I tap on any tab and the video stops. No matter which tab I tap on I see the message "TAB CHANGED". But as soon as I tap on the favorites tab and I move to another, I stop seeing the "TAB CHANGED" message. If I then move to the home screen and play the video and then move to a different tab, the video no longer stops.
The didSelect on the 4th tab is cancelling the didSelect on the 1st tab.
How can I get both of them to work? I have placed them on both view controllers because on the first one I need to reference the video and on the 2nd one I need to reference the list view controller (the 4th view controller actually has two top tabs which switch between a favorites vc and a downloads vc).
UPDATE:
I moved the delegate to the TabBarController subclass. I added the following:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
let hvc = HomeViewController()
if tabBarIndex != 0 {
hvc.stopPlayer()
}
}
I added
delegate = self
and
class TabBarViewController: UITabBarController, UITabBarControllerDelegate {
In the HomeViewController I added this:
func stopPlayer() {
print("TAB CHANGED")
if let jwp = jwPlayer {
jwp.stop()
}
}
however when I change tabs, jwPlayer is always nil.
It seems like you are handling the UITabBarControllerDelegate calbacks in multiple places and for that to happen, you are also changing following multiple times -
self.tabBarController?.delegate = self
Here's what you should do -
Handle UITabBarControllerDelegate in one place.
Dispatch the necessary work calls from there to relevant screens.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarIndex == 0 {
homeVC.startPlayer()
}
else {
homeVC.stopPlayer()
if tabBarIndex == 3 {
let userId = UserDefaults.standard.integer(forKey: "userId")
favoritesVC.updateData(with: userId)
}
}
}
This can be handled anywhere, inside your TabBarController subclass if you have one, or any other object that is guaranteed to exist for the application lifetime.
TabBarController’s delegate does not have to be either of the view controllers. It could be some other object (not even a view controller) that could hold weak references to both view controllers, for example, or post a notification etc
I have 5 tabBarItem in my UITabBarController
One scenario, I have to open First index of UITabBarItem by clicking third UITabBarItem
My approach as in below:
extension FiveTabbarController: UITabBarControllerDelegate {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item == (self.tabBar.items!)[2] {
tabBar.selectedItem = (self.tabBar.items!)[0] // ERROR
self.selectedIndex = 0 // NOT WORKING
}
}
}
Error: *** Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason:
'Directly modifying a tab bar managed by a tab bar controller is not allowed.'
Kindly guide me how to achieve this.
You don't want your view controller's base class to be a UITabBarDelegate. If you were to do that, all of your view controller subclasses would be tab bar delegates. What I think you want to do is to extend UITabBarController
class FiveTabbarController: UITabBarController, UITabBarControllerDelegate {
then, in that class, override viewDidLoad and in there set the delegate property to self:
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
now this class is both a UITabBarDelegate (because UITabBarController implements that protocol), and UITabBarControllerDelegate, and you can override/implement those delegate's methods as desired, such as:
extension FiveTabbarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if let getSelectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController), getSelectedIndex == 2 {
self.selectedIndex = 0
}
}
}
You can achieve the behavior by doing:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if selectedIndex == 2 {
self.selectedIndex = 0
}
}
The reason for the error is because you shouldn't modify the selectedItem, only the index. By modifying the index the tab bar controller will set the selectedItem.
Edited
Didn't notice you are using tabBar's method instead of tabbarcontroller's.
I am implementing an iOS App with UITabBar with UINavigationViewController in Swift. Now facing an issue,
If I select first tab, I can see 'A' ViewController, and on click of any contents of 'A', I redirect to 'B' UINavigationViewController, Now If I click on Second tab, and then again Clicks the first tab, It is showing 'B' NavigationViewController. Expected is, It should display 'A' ViewController. How to achieve that?
Try implementing didSelectViewController delegate and then on selection of 'A ViewController' index redirect to root viewcontroller.
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
let index : Int = (tabBarController.viewControllers?.indexOf(viewController))!
if index == 0
{
let navigationController = viewController as? UINavigationController
navigationController?.popToRootViewControllerAnimated(true)
}
}
Download Sample
#IBAction func itemB(sender: UIButton) {
// do something
self.tabBarController?.selectedIndex = 0
}
In Swift 3.1
Add UITabBarControllerDelegate to your TabBar Class:
class YourClass: UITabBarController, UITabBarControllerDelegate {
After:
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
let yourView = self.viewControllers![self.selectedIndex] as! UINavigationController
yourView .popToRootViewControllerAnimated(false)
}
Currently I have a Tab Bar Controller that is connected to a tableview controller. I'm trying to go to the top of the tableview when I press the tab bar item. I know how to get to the top of the tableview. I just don't know how to do an action when the item is pressed.
You should use UITabBarDelegate with method didSelectItem. Use it as any standard delegate:
class yourclass: UIViewController, UITabBarDelegate {
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
//This method will be called when user changes tab.
}
}
And do not forget to set your tab bar delegate to self in view controller.
Here is an answer to this question
Basically you do this:
Make sure your view controller is subscribed to the UITabBarDelegate
Set tags in IB for each tab bar item
Implement the didSelectItem method, something like this:
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
if(item.tag == 1) {
// Code for item 1
}
else if(item.tag == 2) {
// Code for item 2
}
}
This will give you access to each tab item tapped event. Hope it helps!
In Swift:
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if(item.tag == 1) {
// Code for item 1
} else if(item.tag == 2) {
// Code for item 2
}
}
SWIFT 3
class yourclass: UIViewController, UITabBarDelegate {
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("Test")
}
}
And do not forget to set your tabBar delegate to self in viewDidLoad
override func viewDidLoad(){
super.viewDidLoad()
<YOUR TAB BAR NAME>.delegate = self
}
I was having trouble implementing the other answers here. This is a fuller answer. It assumes you are using a UITabBarController (the default if you create a new Tabbed App). This solution will print a message every time a view controller tab button is tapped.
Code
Create a new Swift file called MyTabBarController.swift. Paste in the following code.
import UIKit
class MyTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// tell our UITabBarController subclass to handle its own delegate methods
self.delegate = self
}
// called whenever a tab button is tapped
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController is FirstViewController {
print("First tab")
} else if viewController is SecondViewController {
print("Second tab")
}
}
}
Interface Builder
On your storyboard select the Tab Bar Controller. Then in the Identity inspector, set the class name to MyTabBarController (that is, the name of the class in the code above).
That's all. You can run your app now and be notified whenever the user taps a tab bar item.
Notes
If you need to run a method on a tap, then you can do something like the following in didSelect method.
if let firstVC = viewController as? FirstViewController {
firstVC.doSomeAction()
}
You could do make the FirstViewController implement the delegate and handle everything there. That way you wouldn't need to make any custom UITabBarController subclass and set it in IB. However, having a child do the parent's work seems like the wrong place to do it. Anyway, here is is:
class FirstViewController: UIViewController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// ...
}
}
The didSelect method above gets called no matter which tab is tapped.
UITabBarControllerDelegate documentation
An alternate solution is to just do something in viewDidAppear in whichever View Controller the tab shows.
First Tab View Controller
import UIKit
class FirstViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("First tab")
}
}
Second Tab View Controller
import UIKit
class SecondViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("Second tab")
}
}
class TestViewController: UIViewController,UITabBarDelegate {
#IBOutlet weak var tabbar: UITabBar!
override func viewDidLoad() {
super.viewDidLoad()
tabbar.delegate = self
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(tabBar.items![1]) // The number is tab index
}
}
I want to call a method on a ViewController (type "MatchesViewController", which is on the 3rd tab of my TabBarViewControlleer) if the selected item is NOT of that class.
This is my delegate function for listening when a Tab Bar Item was changed.
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
}
Inside this code, I'd like to detect if the item selected (and its View Controller) is of type "MatchesViewController". If it is NOT of this type, then call method on that controller.
Why not use tabBarController(_:didSelectViewController:) ?
override func tabBarController(tabBarController: UITabBarController,
didSelectViewController viewController: UIViewController)
{
if !(viewController is MatchesViewController) {
let matchesVC = tabBarController.viewControllers?[2] as MatchesViewController
matchesVC.refresh()
}
}