21

Here is what I am trying to do: enter image description here

Note: The screenshot is taken from an earlier version of iOS

What I have been able to achieve: enter image description here

Code:

 override func viewWillAppear(animated: Bool) {
    // Creates image of the Button
    let imageCameraButton: UIImage! = UIImage(named: "cameraIcon")

    // Creates a Button
    let cameraButton = UIButton(type: .Custom)
    // Sets width and height to the Button
    cameraButton.frame = CGRectMake(0.0, 0.0, imageCameraButton.size.width, imageCameraButton.size.height);
    // Sets image to the Button
    cameraButton.setBackgroundImage(imageCameraButton, forState: .Normal)
    // Sets the center of the Button to the center of the TabBar
    cameraButton.center = self.tabBar.center
    // Sets an action to the Button
    cameraButton.addTarget(self, action: "doSomething", forControlEvents: .TouchUpInside)

    // Adds the Button to the view
    self.view.addSubview(cameraButton)
}

I did try to create a rounded button in the normal way, but this was the result:

enter image description here

Code Snippet for rounded button:

//Creation of Ronded Button
    cameraButton.layer.cornerRadius = cameraButton.frame.size.width/2
    cameraButton.clipsToBounds = true
Supratik Majumdar
  • 2,365
  • 1
  • 23
  • 31
  • 1
    That is done by adding a button over the TabBar – E-Riddie Mar 15 '16 at 14:34
  • The is not possible as, you cannot add a button to the Tab Bar Controller View. Moreover, any button added to either of the tab views the button is always behind the tab Bar. In addition to that if I added a button to a single view, it will not be available in all the views as in my case described above. – Supratik Majumdar Mar 15 '16 at 14:42
  • Not possible :D? I was telling you the idea at first, as you are adding the view at the wrong controller. For this you need to subclass `UITabBarController` and make your calculations there. You have the answer updated – E-Riddie Mar 15 '16 at 16:03
  • Yes I misunderstood. Thanks @EridB – Supratik Majumdar Mar 15 '16 at 16:38

4 Answers4

53

Solution

You need to subclass UITabBarController and then add the button above TabBar's view. A button action should trigger UITabBarController tab change by setting selectedIndex.

Code

The code below only is a simple approach, however for a full supporting iPhone (including X-Series)/iPad version you can check the full repository here: EBRoundedTabBarController

class CustomTabBarController: UITabBarController {

    // MARK: - View lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        let controller1 = UIViewController()
        controller1.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
        let nav1 = UINavigationController(rootViewController: controller1)

        let controller2 = UIViewController()
        controller2.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 2)
        let nav2 = UINavigationController(rootViewController: controller2)

        let controller3 = UIViewController()
        let nav3 = UINavigationController(rootViewController: controller3)
        nav3.title = ""

        let controller4 = UIViewController()
        controller4.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 4)
        let nav4 = UINavigationController(rootViewController: controller4)

        let controller5 = UIViewController()
        controller5.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 5)
        let nav5 = UINavigationController(rootViewController: controller5)


        viewControllers = [nav1, nav2, nav3, nav4, nav5]
        setupMiddleButton()
    }

    // MARK: - Setups

    func setupMiddleButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))

        var menuButtonFrame = menuButton.frame
        menuButtonFrame.origin.y = view.bounds.height - menuButtonFrame.height
        menuButtonFrame.origin.x = view.bounds.width/2 - menuButtonFrame.size.width/2
        menuButton.frame = menuButtonFrame

        menuButton.backgroundColor = UIColor.red
        menuButton.layer.cornerRadius = menuButtonFrame.height/2
        view.addSubview(menuButton)

        menuButton.setImage(UIImage(named: "example"), for: .normal)
        menuButton.addTarget(self, action: #selector(menuButtonAction(sender:)), for: .touchUpInside)

        view.layoutIfNeeded()
    }


    // MARK: - Actions

    @objc private func menuButtonAction(sender: UIButton) {
        selectedIndex = 2
    }
}

Output

enter image description here

E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • 1
    How to hide this button if we moved from tabBarController? – Alexander Khitev Jun 14 '16 at 06:49
  • @Alexsander What do you mean if we moved from tabBarController? – E-Riddie Jun 14 '16 at 08:06
  • 1
    for example if we moved or showed another screen. I made this via delegate, but I think that it isn't correct. – Alexander Khitev Jun 14 '16 at 08:13
  • 1
    @Alexsander For that you can use `viewWillDisappear` to hide the button, and `viewWillAppear` to show it. Declare a instance variable to hold the button instance so you can hide and show it when you navigate through the app. – E-Riddie Jun 14 '16 at 13:07
  • @EridB: Would you still have the example project of the above solution? – Loebre Sep 02 '16 at 17:53
  • 1
    @Loebre This code is the whole project. There is nothing fancy, just set this controller as rootController under AppDelegate and you're done – E-Riddie Sep 02 '16 at 18:17
  • 1
    For some reason, my menu button keeps going behind the tab bar if I have `hidesBottomBarWhenPushed = true` on a view controller and go back in the navigation stack from the view controller with that flag on. I tried placing a few `view.bringSubview(toFront: menuButton)` but that didn't help. Any ideas? – Raymond26 Nov 16 '16 at 01:37
  • 1
    same with me. did anyone figured that out? – Yaroslav Dukal Apr 15 '17 at 09:00
  • 5
    @Raymond26 @alizain-prasla I wrote a blog about this issue and how to make it work with `hidesBottomBarWhenPushed`: https://equaleyes.com/blog/2017/09/04/the-common-raised-center-button-problems-in-tabbar/ – Dejan Skledar Sep 15 '17 at 08:52
  • @MaksimKniazev make sure you set the the zPosition of the button's layer to 100 or something and it'll work. – animaonline Dec 05 '17 at 12:51
  • the button view view beyond tab bar is not getting click event...Please help – Hardik Amal Dec 16 '17 at 14:32
  • Has anyone tried this with iPhone X? When I run it on the simulator the circular button gets pushed to the bottom of the screen. – Chace Jan 03 '18 at 15:38
  • It's important to note that you should disable the tab that the button is overlaying (e.g. `nav3.tabBarItem.isEnabled = false`) so the user cannot click behind the button. – Justin Rose Apr 26 '18 at 15:42
  • How to show some other controller with tabbar visible @E-Riddie – Manish Kumar Sep 21 '21 at 01:34
13

Swift 3 Solution

With a slight adjustment to EricB's solution to have this work for Swift 3, the menuButton.addTarget() method needs to have it's selector syntax changed a bit.

Here is the new menuButton.addTarget() function:

menuButton.addTarget(self, action: #selector(MyTabBarController.menuButtonAction), for: UIControlEvents.touchUpInside)

When defining my TabBarController class, I also add a UITabBarControllerDelegate and placed all of the that in the

override func viewDidAppear(_ animated: Bool) { ... }

For extra clarity, the full code is:

Full Code Solution

import UIKit

class MyTabBarController: UITabBarController, UITabBarControllerDelegate {

// View Did Load
override func viewDidLoad() {
    super.viewDidLoad()

}

// Tab Bar Specific Code
override func viewDidAppear(_ animated: Bool) {
    let controller1 = UIViewController(self.view.backgroundColor = UIColor.white)
    controller1.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 1)
    let nav1 = UINavigationController(rootViewController: controller1)

    let controller2 = UIViewController()
    controller2.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 2)
    let nav2 = UINavigationController(rootViewController: controller2)

    let controller3 = UIViewController()
    let nav3 = UINavigationController(rootViewController: controller3)
    nav3.title = ""

    let controller4 = UIViewController()
    controller4.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 4)
    let nav4 = UINavigationController(rootViewController: controller4)

    let controller5 = UIViewController()
    controller5.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 5)
    let nav5 = UINavigationController(rootViewController: controller5)

    self.viewControllers = [nav1, nav2, nav3, nav4, nav5]
    self.setupMiddleButton()
}

// TabBarButton – Setup Middle Button
func setupMiddleButton() {
    let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
    var menuButtonFrame = menuButton.frame
    menuButtonFrame.origin.y = self.view.bounds.height - menuButtonFrame.height
    menuButtonFrame.origin.x = self.view.bounds.width / 2 - menuButtonFrame.size.width / 2
    menuButton.frame = menuButtonFrame

    menuButton.backgroundColor = UIColor.red
    menuButton.layer.cornerRadius = menuButtonFrame.height/2
    self.view.addSubview(menuButton)

    menuButton.setImage(UIImage(named: "example"), for: UIControlState.normal)
    menuButton.addTarget(self, action: #selector(MyTabBarController.menuButtonAction), for: UIControlEvents.touchUpInside)

    self.view.layoutIfNeeded()
}

// Menu Button Touch Action
func menuButtonAction(sender: UIButton) {
    self.selectedIndex = 2
    // console print to verify the button works
    print("Middle Button was just pressed!")
   }
 }
Brdjx
  • 131
  • 1
  • 5
7

This is the customTabbarcontroller class which is the subclass of UITabbarcontroller. It's the same idea as given by @EridB. But in his code @Raymond26's issue wasn't solved. So, posting a complete solution written in Swift 3.0

protocol CustomTabBarControllerDelegate
{
    func customTabBarControllerDelegate_CenterButtonTapped(tabBarController:CustomTabBarController, button:UIButton, buttonState:Bool);
}

class CustomTabBarController: UITabBarController, UITabBarControllerDelegate
{
    var customTabBarControllerDelegate:CustomTabBarControllerDelegate?;
    var centerButton:UIButton!;
    private var centerButtonTappedOnce:Bool = false;

    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews();

        self.bringcenterButtonToFront();
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()

        self.delegate = self;

        self.tabBar.barTintColor = UIColor.red;

        let dashboardVC = DashboardViewController()        
        dashboardVC.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
        let nav1 = UINavigationController(rootViewController: dashboardVC)

        let myFriendsVC = MyFriendsViewController()
        myFriendsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .featured, tag: 2)
        let nav2 = UINavigationController(rootViewController: myFriendsVC)

        let controller3 = UIViewController()
        let nav3 = UINavigationController(rootViewController: controller3)
        nav3.title = ""

        let locatorsVC = LocatorsViewController()
        locatorsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 4)
        let nav4 = UINavigationController(rootViewController: locatorsVC)

        let getDirectionsVC = GetDirectionsViewController()
        getDirectionsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .history, tag: 5)
        let nav5 = UINavigationController(rootViewController: getDirectionsVC)

        viewControllers = [nav1, nav2, nav3, nav4, nav5]

        self.setupMiddleButton()
    }

    // MARK: - TabbarDelegate Methods

    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
    {
        switch viewController
        {
        case is DashboardViewController:
            self.showCenterButton()
        case is MyFriendsViewController:
            self.showCenterButton()
        case is GetDirectionsViewController:
            self.showCenterButton()
        case is LocatorsViewController:
            self.showCenterButton()
        default:
            self.showCenterButton()
        }
    }

    // MARK: - Internal Methods

    @objc private func centerButtonAction(sender: UIButton)
    {
        //        selectedIndex = 2
        if(!centerButtonTappedOnce)
        {
            centerButtonTappedOnce=true;
            centerButton.setImage(UIImage(named: "ic_bullseye_white"), for: .normal)
        }
        else
        {
            centerButtonTappedOnce=false;
            centerButton.setImage(UIImage(named: "ic_bullseye_red"), for: .normal)
        }

        customTabBarControllerDelegate?.customTabBarControllerDelegate_CenterButtonTapped(tabBarController: self,
                                                                                          button: centerButton,
                                                                                          buttonState: centerButtonTappedOnce);
    }

    func hideCenterButton()
    {
        centerButton.isHidden = true;
    }

    func showCenterButton()
    {
        centerButton.isHidden = false;
        self.bringcenterButtonToFront();
    }

    // MARK: - Private methods

    private func setupMiddleButton()
    {
        centerButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))

        var centerButtonFrame = centerButton.frame
        centerButtonFrame.origin.y = view.bounds.height - centerButtonFrame.height
        centerButtonFrame.origin.x = view.bounds.width/2 - centerButtonFrame.size.width/2
        centerButton.frame = centerButtonFrame

        centerButton.backgroundColor = UIColor.red
        centerButton.layer.cornerRadius = centerButtonFrame.height/2
        view.addSubview(centerButton)

        centerButton.setImage(UIImage(named: "ic_bullseye_red"), for: .normal)
        centerButton.setImage(UIImage(named: "ic_bullseye_white"), for: .highlighted)
        centerButton.addTarget(self, action: #selector(centerButtonAction(sender:)), for: .touchUpInside)

        view.layoutIfNeeded()
    }

    private func bringcenterButtonToFront()
    {
        print("bringcenterButtonToFront called...")
        self.view.bringSubview(toFront: self.centerButton);
    }

}

This is the DashboardViewController for complete reference:

class DashboardViewController: BaseViewController, CustomTabBarControllerDelegate
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        (self.tabBarController as! CustomTabBarController).customTabBarControllerDelegate = self;
    }

    override func viewWillAppear(_ animated: Bool)
    {
        super.viewWillAppear(animated);
        (self.tabBarController as! CustomTabBarController).showCenterButton();
    }

    override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated);

        self.hidesBottomBarWhenPushed = false;
        (self.tabBarController as! CustomTabBarController).hideCenterButton();
    }

    override func viewWillLayoutSubviews()
    {
        super.viewWillLayoutSubviews();

        if(!isUISetUpDone)
        {
            self.view.backgroundColor = UIColor.lightGray
            self.title = "DASHBOARD"
            self.prepareAndAddViews();
            self.isUISetUpDone = true;
        }
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }

    //MARK: CustomTabBarControllerDelegate Methods

    func customTabBarControllerDelegate_CenterButtonTapped(tabBarController: CustomTabBarController, button: UIButton, buttonState: Bool)
    {
        print("isDrive ON : \(buttonState)");
    }

    //MARK: Internal Methods

    func menuButtonTapped()
    {
        let myFriendsVC = MyFriendsViewController()
        myFriendsVC.hidesBottomBarWhenPushed = true;
        self.navigationController!.pushViewController(myFriendsVC, animated: true);
    }

    //MARK: Private Methods

    private func prepareAndAddViews()
    {
        let menuButton = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
        menuButton.titleLabel?.text = "Push"
        menuButton.titleLabel?.textColor = UIColor.white
        menuButton.backgroundColor = UIColor.red;
        menuButton.addTarget(self, action: #selector(DashboardViewController.menuButtonTapped), for: .touchUpInside)
        self.view.addSubview(menuButton);
    }
}
Vineet
  • 161
  • 1
  • 8
  • 1
    thanks a lot it saved my time. I have one issue, when I click on the custom centre button, I want the background to go blur and control should be on the same controller(I don't want black screen, check Asana app for more). Can you suggest me how to achieve the same? – Satish May 30 '17 at 11:54
0

with StoryBoard: Click the tab bar button within the view controller of the particular tab bar item you want to make prominent,

Remove the text, just set the image inset top to -25 of the tab bar button. Check Like Below image enter image description here

Shahzaib Maqbool
  • 1,479
  • 16
  • 25