Curved Tabbar with a round middle button – Swift

Tab Bars are an essential component and one of the most commonly used features in iOS app design. It provides easy navigation and enhances the look and feel.

Enhancing the Tab bar with customized sleek designs can instantly make your app stand out.

Set up the storyboard…

I assume you have created your new iOS project. Before we start, just set up your viewcontrollers.

  1. Add a TabBarController into your Storyboard.
  2. Add 3 or 5 ViewControllers and link them to the TabBarController

Create a new class MyTabBarCtrl.swift

import UIKit
class MyTabBarCtrl: UITabBarController, UITabBarControllerDelegate {
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!

    override func viewDidLoad() {
        self.delegate = self

Go back to storyboard, and change your TabBarController class to MyTabBarCtrl.

Draw the TabBar shape

We need to subclass the TabBar. Create a new class MyTabBar.swift

import UIKit
class MyTabBar: UITabBar {


The shape layer was inspired from this article. Add the following to MyTabBar class.

private var shapeLayer: CALayer?
private func addShape() {
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = createPath()
    shapeLayer.strokeColor = UIColor.lightGray.cgColor
    shapeLayer.fillColor = UIColor.white.cgColor
    shapeLayer.lineWidth = 1.0
    //The below 4 lines are for shadow above the bar. you can skip them if you do not want a shadow
    shapeLayer.shadowOffset = CGSize(width:0, height:0)
    shapeLayer.shadowRadius = 10
    shapeLayer.shadowColor = UIColor.gray.cgColor
    shapeLayer.shadowOpacity = 0.3

    if let oldShapeLayer = self.shapeLayer {
        self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
    } else {
        self.layer.insertSublayer(shapeLayer, at: 0)
    self.shapeLayer = shapeLayer
override func draw(_ rect: CGRect) {
func createPath() -> CGPath {
    let height: CGFloat = 37.0
    let path = UIBezierPath()
    let centerWidth = self.frame.width / 2
    path.move(to: CGPoint(x: 0, y: 0)) // start top left
    path.addLine(to: CGPoint(x: (centerWidth - height * 2), y: 0)) // the beginning of the trough

    path.addCurve(to: CGPoint(x: centerWidth, y: height),
    controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))

    path.addCurve(to: CGPoint(x: (centerWidth + height * 2), y: 0),
    controlPoint1: CGPoint(x: centerWidth + 35, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))

    path.addLine(to: CGPoint(x: self.frame.width, y: 0))
    path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
    path.addLine(to: CGPoint(x: 0, y: self.frame.height))

    return path.cgPath

That’s all for the shape!

But there’s a tiny problem. The middle button is raised a little beyond the bounds of the TabBar view. By default, click events won’t be caught on views outside of the bounds of it’s superview. To solve this, we override hitTest:withEvent: method. Add thin in the same MyTabBar class

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
        for member in subviews.reversed() {
            let subPoint = member.convert(point, from: self)
            guard let result = member.hitTest(subPoint, with: event) else { continue }
            return result
        return nil

Go back to your storyboard. Select TabBar object under MyTabBarCtrl. and under Identity Inspector, Select MyTabBar,

Screenshot 2019-02-22 at 11.46.29 PM.pngScreenshot 2019-02-22 at 11.48.43 PM.png

The shape of the tab bar should now be visible in your view controller in the storyboard.

Design the middle button

Open MyTabBarCtrl.swift and add this function

    // TabBarButton – Setup Middle Button
    func setupMiddleButton() {

        let middleBtn = UIButton(frame: CGRect(x: (self.view.bounds.width / 2)-25, y: -20, width: 50, height: 50))
        middleBtn.setIcon(icon: .fontAwesomeSolid(.home), iconSize: 20.0, color: UIColor.white, backgroundColor: UIColor.white, forState: .normal)
        middleBtn.applyGradient(colors: colorBlueDark.cgColor,colorBlueLight.cgColor])
        //add to the tabbar and add click event
        middleBtn.addTarget(self, action: #selector(self.menuButtonAction), for: .touchUpInside)


    // Menu Button Touch Action
    @objc func menuButtonAction(sender: UIButton) {
        self.selectedIndex = 2   //to select the middle tab. use "1" if you have only 3 tabs.

For setIcon, I used this library.

For the button gradient, check out this tutorial.

Call this function in your TabBarController’s viewDidLoad().

That’s all!

Also published on Medium.

By |2020-02-03T07:34:38+00:00March 14th, 2019|Categories: iOS Tutorials|Tags: , , |13 Comments


  1. Oscar August 4, 2019 at 11:23 am - Reply

    I have used this, and works fine but, If you want to get text with icons in landscape mode the centered item, even if there’s no text is not in de middle

  2. jeevan October 4, 2019 at 9:57 am - Reply


  3. fggfdgfgf January 8, 2020 at 2:33 pm - Reply

    Zeba why don’t you have a screenshot of the final result?

    • Zeba Rahman May 16, 2020 at 1:34 pm - Reply

      added. thanks!

  4. Toàn February 13, 2020 at 8:34 am - Reply

    setIcon hear, you can push source code in githubwhere are setIcon. you can push source code up github, pleases.

  5. Toàn February 13, 2020 at 8:35 am - Reply

    where are setIcon. you can push source code up github, pleases.

  6. Vibhor May 6, 2020 at 9:28 pm - Reply

    Hello, thanks for such a great guild.
    I need some help here. I have followed the article and implemented the same in my application. I want to show a popup when user clicked on the middle button instead of just opening it as a tab bar view. Can you please help me with this. I tried to implement it using shouldSelect viewController but it doesnt work and the callback does not come here when i click on the middle button.

    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
    if viewController.isKind(of: MyButtonController.self) {
    let vc = MyButtonController()
    vc.modalPresentationStyle = .overFullScreen
    self.present(vc, animated: true, completion: nil)
    return false
    return true

    • Zeba Rahman May 16, 2020 at 1:33 pm - Reply

      try this:
      Remove the code line inside func menuButtonAction(sender: UIButton) { } method,
      and add there your own code for pop up.

  7. Ed May 7, 2020 at 4:24 pm - Reply

    Nice article, it’d be great if a github repo was available to see the whole thing as I think I’m missing a few pieces to the puzzle.

    • Zeba Rahman May 16, 2020 at 1:31 pm - Reply

      I’ll find some time and make a repo, thanks for suggesting.

  8. Vikas Nayak May 13, 2020 at 4:18 am - Reply

    It’s working as shown. Finally awesome.

Leave A Comment