Action not working if UITabBar has more than 5 items - ios

I want to perform an action when the user clicked on some tabs in UITabBar without opening another view. For example, setting tab or share.
Here is what I did :
class ViewTabBarController: UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// Do any additional setup after loading the view.
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("do something")
}
}
This code works fine if the UITabBar has just 5 items.
But the problem here is that if UITabBar has more than 5 items those that are under the "More Tab" did not call the tabBar() function when clicked.

From the documentation description of the didSelect method of UITabBarControllerDelegate:
Tells the delegate that the user selected an item in the tab bar.
What that means is that the method is called when the user taps on one of the buttons in the bar. When you have a 'more' button then that is the button in the tab bar so tapping 'more' fires that method.
The view controllers in the 'more' section are actually processed in a different way involving the use of a UINavigationController. With these views they don't have a button in the tab bar and therefore this even is not fired.

Related

Tab bar item as button

I have 5 tab bar items in my tab bar, 4 of which have segues to navigation controllers which lead to view controllers. I want to make the middle tab bar item act as a button, so that when I click on it, I have control over what happens.
Currently, my middle tab bar item is also connected to a navigation controller, which is not right because now when I click the tab bar item, it opens a black navigation controller. How can I convert the middle tab bar item to act as a button, rather than going to a navigation controller?
If I remove the navigation controller, it also removes the tab bar item from the tab bar.
If you want your tab bar item to act as a button you could subclass a UITabBarController and implement this delegate function:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
// Check if bar item selected is center
if viewController.tabBarItem.tag == 2 {
// Do Something Here ...
// Present View Controller
guard let navigationController = storyboard?.instantiateViewController(withIdentifier: "NavigationController") as? UINavigationController else { return false }
present(navigationController, animated: true)
// Returning false will not open the connected tab bar item view controller you attached to it
return false
}
// Return true to open the connected tab bar item view controller you attached to it (e.x. everything but the center item)
return true
}
To implement a custom tab bar and use tab bar items like a normal button
Add a new Tab Bar view to your view controller (VC)
Add the custom tab bar items you need and assign tag numbers on them (On Attribute inspector)
Add a delegate outlet from Tab bar View to your view controller (Right Click and drag To VC)
On your view controller, subclass UITabBarDelegate, like this
class ViewController: UIViewController, UITaBarDelegate {}
Implement this delegate function, then it should works
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 1 {
//Do something
}
if item.tag == 2 {
//Do something
}
.......
}

Detecting second tap on tab bar item when first one triggers popToRootViewController

I want to implement scrolling to top of view controller when user taps on tab bar item in tab bar. I do it by overriding tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool method in UITabBarControllerDelegate.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
// Scroll to top only if we are already on given tab
if viewControllers?.firstIndex(of: viewController) == selectedIndex {
// Checks if root VC of navigation controller is currently shown and then scrolls it to top
scrollToTop(at: selectedIndex)
}
return true
}
Each tab has navigation controller as its view controller and tapping on tab bar causes pop to root view controller. It's fine and desired, but I want a double tap to work, i.e. user is in second (or later) view in navigation controller, double taps on tab bar and then navigation controller pops and root view controller scrolls to top (first tap pops to root view controller and second one scrolls to top).
Unfortunately, this delegate method is called only once when such scenario happens. It starts detecting next taps on tab bar items only after pop transition ends.
Double tapping works fine when switching tabs, i.e. user is on tab B, double taps tab A, and then first tap switches to tab A and second one scrolls makes it scroll to top.
I checked Facebook and Twitter apps, and both of them correctly handle quick double tap - they pop and then scroll to top.
Is there a way to detect tab bar items taps when pop transition is in progress? I tried overriding tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) of UITabBarController but it's also not called twice on double tap in aforementioned scenario. Adding custom double tap recognizer also does not help as it significantly delays single taps to tab bar which causes app to look laggy.
You can try using Notification for this case.
First, create extension for your Notification.Name :
extension Notification.Name {
static var userDoubleTapTabbar: Notification.Name {
return .init(rawValue: "User.DoubleTapTabbar")
}
}
Then, In your Tabbar Controller, detect your user double tap items event and fire notification:
public var lastTabbarItem: UITabBarItem?
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
SEManager.shared.playSound(type: .tap_bottom_tap)
if self.lastTabbarItem == item {
NotificationCenter.default.post(name: .userDoubleTapTabbar, object: nil)
}
self.lastTabbarItem = item
}
Then in your Tabbar item view controller, you can add observer & handler for that notification to scroll your table view to top. You can use this to check if that Tabbar item view controller is current visible:
if viewController.viewIfLoaded?.window != nil {
// viewController is visible
}
Hope it helps and sorry for my bad English.

Navigation bar item as tab bar item

I did search something similar with my question, but didn't find anything to help me solve my issue.
Is it possible to set behavior for navigation bar item as tab bar item?
I use TabBarController. Each tab item should show the corresponding ViewController. When user tap on the item in nav bar it should show the corresponding ViewController too with the bottom tab bar. And this ViewController should be as tab bar controller, but not display tab item in the bottom tab bar.
How I can achieve the scenario describe above? Or any alternative idea how it possible to implement, please! Almost always the top and bottom bars should be visible for each ViewController.
Drag and drop from your bar button item in IB to your class file and define an outlet, then in viewDidLoad create a gesture recognizer, add an action to your gesture recognizer and you are all set. I hope it helps.
class SomeClass: UIViewControler {
let someBarButton:UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(SomeClass.tapFunction))
someBarButton.addGestureRecognizer(tap)
}
func tapFunction() {
//take me to a view controller
self.performSegue(withIdentifier: "myIdentifier", sender: self)
}
}

swift check which tab bar item was clicked

I am using a tab bar controller and I wonder if there is a way to check which tab is being clicked?
If the user clicks on the "account" tab and is not logged in I want to redirect to a full screen modal login screen instead of the account VC.
You can do it in your custom UITabBarController or somewhere, and override the 'didSelectItem' function.
import UIKit
class TabbarViewController: UITabBarController {
override func viewDidLoad() {
}
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
print("Selected Index :\(self.selectedIndex)");
}
}
In the scenario you outlined, I would check to see if current user is logged in or not, and if not segue to the appropriate screen of your application.
UITabBarDelegate's didSelectItem
The options that others have provided are fine, but I wanted to let you know of another way. In the viewWillAppear, viewDidAppear, or viewDidLoad functions, you can call what you need to segue to a login ViewController

Get the title of the tab selected in UITabBarController at application launch

I am trying to get the tab title at application launch.
I can do this to read the tabBarItem.title when the user changes tabs:
func tabBarController(
tabBarController: UITabBarController,
didSelectViewController viewController: UIViewController) {
UserActivity.trackScreen(name: viewController.tabBarItem.title)
}
This method does not fire for the initial selection. I tried this approach in UITabVarController's viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
UserActivity.trackScreen(name: self.selectedItem.title) // I think this is not set yet, it is nil.
}
This does not work.
How do I get the selected tab bar item, or the tab bar item that will be selected, on app launch?
Doing an action on the "selected" tab is a special case for the first launch, because the delegate method didSelectViewController does not fire.
If (and this might be a big "if") you can assume the first tab is the one that is selected on app launch, this code will work for handling the first-launch case:
if let vcs = self.viewControllers {
var firstVC = vcs[0] as UIViewController
UserActivity.trackScreen(name: firstVC.tabBarItem.title)
}
This works for me. Open to better answers.

Resources