Tab bar item as button - ios

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
}
.......
}

Related

Present a Tab Bar View Modally when View Controller is embedded in a Navigation Controller?

My storyboard is arranged as so.
Red: Tab Bar Controller that segues to...
Orange: Nav Controllers that have embedded...
Green: View Controllers
I want to make my Middle Tab View (green) present itself modally, sort of like how the reddit app does it with its middle 'Post to Reddit' button. When this Middle View is dismissed the original Tab that was open beforehand will be returned to. How can this be done?
One way to do this...
Give the UINavigationController that is your 2nd tab a StoryboardID - such as "createItemsNavController" - then implement shouldSelect in your custom tab bar controller class.
If the 2nd tab is selected (tabs, like all arrays, are Zero based), instantiate your "createItemsNavController" and present it, returning false for shouldSelect:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard let indexOfTab = viewControllers?.firstIndex(of: viewController) else {
return true
}
if indexOfTab == 1 {
if let vc = storyboard?.instantiateViewController(withIdentifier: "createItemsNavController") as? UINavigationController {
present(vc, animated: true, completion: nil)
}
return false
}
return true
}
If you're going that route, you could also (and it might be a good idea to) replace that Tab connection in your Storyboard with a blank view controller... as you probably want to avoid having it loaded by the tab bar controller, even if you never allow that tab to be activated.
As a side note: that could be a very confusing UX. Users (and Apple) like apps that conform to common interface actions. Since users are familiar with tab bars, where selecting a tab button switches to that tab, changing the functionality in this way may be frowned upon.
Of course, it's your app, and your design choice...

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)
}
}

How to force a tab bar item to go to the root level of that item whenever its tapped

I'm new to iOS and Swift. In my storyboard I've set up a tab bar controller where the right most item is a "More" item that leads to a table view controller embedded in a navigation controller. Each item in the table view (static) goes to a view controller (say "View 1", "View 2", and "View 3").
The default behavior is as follows. Let's say I tap "More". Then I'm looking at the table with cells for "View 1", "View 2", and "View 3". Then let's say I tap "View 1". If I then click a different item in the tab bar controller, and then click "More" again, rather than take me to the table view, it will take me to "View 1" since that's the last thing I tapped when I was last in "More". I'd like the behavior to be that if I tap outside of "More" with another tab bar element, any time I tap "More" again, I want it to always take me to the table view, regardless of what I was viewing previously within "More".
Essentially, I want "More" to forget its state or reset its state.
How do I force this to happen?
I will walk you through an example to make sure that it is clear.
Consider that this is the storyboard:
It has a tabbar view controller connected to:
A view controller (the one on the top).
A table view controller which is embeded in navigation controller (the one on the bottom). It has tableview with a static cell, when tap on it, it pushes to another view controller.
So far so good! now, the first view controller class should conforms to UITabBarControllerDelegate and implements tabBarController(_:didSelect:) method, as follows:
class ViewController: UIViewController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// im my example the desired view controller is the second one
// it might be different in your case...
let secondVC = tabBarController.viewControllers?[1] as! UINavigationController
secondVC.popToRootViewController(animated: false)
}
}
Remark: if you have more that tow view controllers connected to the tabbar view controller, you should apply this code to the first view controller that should appear in the tabbar view controller.
And that's it!
Output:
Cheers up! hope this helped.
The code supplied works very well with one exception. If you have multiple tabbar items linked to one view controller then the indexed code tabBarController.viewControllers?[1] will only work on "1". It is possible to have "2" and "3" etc. By adding tabBarController.selectedIndex as the index it will always return back to root for that tabbar item. Below is how I fixed the code. I hope this helps someone:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// im my example the desired view controller is the second one
// it might be different in your case...
let secondVC = tabBarController.viewControllers?[tabBarController.selectedIndex] as! UINavigationController
secondVC.popToRootViewController(animated: false)
}
after making the class confirm to the tabBarController delegate just add this function
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
self.navigationController?.popToRootViewController(animated: false)
}

Can a Tab Bar contain a "play" button

In my Tab Bar, I have a "My favorites" and "About Us" icon. These go to different View Controllers. I have made a UIButton that plays an audio file. How do I add that button to the Tab Bar at the bottom?
So when the button is tapped, it plays on the current scene, and does not go to a new scene.
You can prevent a tab from opening it's view controller by subclassing UITabBarController and implementing tabBarController(_:shouldSelectViewController:). From there you could perform your play action.
More on UITabBarControllerDelegate
In Swift your subclass would look like the following
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
let tabIndex = tabBarController.viewControllers?.indexOf(viewController)
if tabIndex == 2 {
// Perform your play action here
return false
}
return true
}
}
You can write a your tabBarController which has several buttons. each button has a tag. so, you can judge the button which you can't go to a new controller.

Resources