skip to Main Content

So this is the navigation my designer made for our project. Height of the TabBar is 70.

curved iOS tab bar

What I have tried so far.
My attempt was based on tutorial from Philipp Weiss.

https://betterprogramming.pub/draw-a-custom-ios-tabbar-shape-27d298a7f4fa

Its based on idea of creating custom IBDesignable UITabBar class and overriding draw method.

@IBDesignable
class CustomizedTabBar: UITabBar {

    private var shapeLayer: CALayer?
    
    override func draw(_ rect: CGRect) {
        self.addShape()
    }

    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.strokeColor = UIColor.blueMenu2.cgColor
        shapeLayer.fillColor = UIColor.blueMenu2.cgColor
        shapeLayer.lineWidth = 1.0

        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }

        self.shapeLayer = shapeLayer
    }


    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

        // first curve down
        path.addCurve(to: CGPoint(x: centerWidth, y: height),
                      controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))
        // second curve up
        path.addCurve(to: CGPoint(x: (centerWidth + height * 2), y: 0),
                      controlPoint1: CGPoint(x: centerWidth + 35, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))

        // complete the rect
        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))
        path.close()

        return path.cgPath
    }

I was trying to edit bezier path to reach my goal but with no success.
I am not sure if this approach can work for this specific TabBar design.

Setting height of navigation to 70 was without problem.

@IBInspectable var height: CGFloat = 70

    override open func sizeThatFits(_ size: CGSize) -> CGSize {
            guard let window = UIApplication.shared.keyWindow else {
                return super.sizeThatFits(size)
            }
            var sizeThatFits = super.sizeThatFits(size)
            if #available(iOS 11.0, *) {
                sizeThatFits.height = height + window.safeAreaInsets.bottom
            } else {
                sizeThatFits.height = height
            }
            return sizeThatFits
        }

How can I create this curved TabBar?

Do u know how to make similar shape just by using bezier curves?

2

Answers


  1. Your subclass likely isn’t working because UITabBar doesn’t draw the tab bar itself in drawRect(). But makes it from multiple internal sub views.

    I’d recommend using a UITabBarController, but hiding the UITabBar itself.

    self.tabBarController.tabBar.hidden = true
    

    Then putting your own custom tab bar look alike view at the button of the screen.
    Adding additionalSafeAreaInsets to make the content move up out of the way of your new view, like they would the real tab bar.

    Then just change the tab index yourself on button presses.

    self.tabBarController.selectedIndex = 1
    
    Login or Signup to reply.
  2. To create a UIBezierPath for your desired shape…

    enter image description here

    • move to 1
    • add 90° clockwise arc with center c1
    • add line to 2
    • add 90° clockwise arc with center c2
    • add 180° counter-clockwise arc with center c3
    • add 90° clockwise arc with center c4
    • add line to 3
    • add 90° clockwise arc with center c5
    • add line to 4
    • add 90° clockwise arc with center c6
    • add line to 5
    • add 90° clockwise arc with center c7
    • close path

    Here is some sample code – it’s a UIView subclass, with all the path elements in layoutSubviews():

    class TabBarShapeView: UIView {
        var shapeLayer: CAShapeLayer!
        override class var layerClass: AnyClass {
            return CAShapeLayer.self
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
        private func commonInit() {
            shapeLayer = self.layer as? CAShapeLayer
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = UIColor.gray.cgColor
            shapeLayer.lineWidth = 1
        }
        override func layoutSubviews() {
            super.layoutSubviews()
            
            let middleRad: CGFloat = bounds.height - 10.0
            
            let cornerRad: CGFloat = 12.0
            
            let pth = UIBezierPath()
            
            let topLeftC: CGPoint = CGPoint(x: bounds.minX + cornerRad, y: bounds.minY + cornerRad)
            let topRightC: CGPoint = CGPoint(x: bounds.maxX - cornerRad, y: bounds.minY + cornerRad)
            let botRightC: CGPoint = CGPoint(x: bounds.maxX - cornerRad, y: bounds.maxY - cornerRad)
            let botLeftC: CGPoint = CGPoint(x: bounds.minX + cornerRad, y: bounds.maxY - cornerRad)
    
            var pt: CGPoint!
    
            // 1
            pt = CGPoint(x: bounds.minX, y: bounds.minY + cornerRad)
            pth.move(to: pt)
            
            // c1
            pth.addArc(withCenter: topLeftC, radius: cornerRad, startAngle: .pi * 1.0, endAngle: .pi * 1.5, clockwise: true)
    
            // 2
            pt = CGPoint(x: bounds.midX - middleRad, y: bounds.minY)
            pth.addLine(to: pt)
    
            // c2
            pt.y += middleRad * 0.5
            pth.addArc(withCenter: pt, radius: middleRad * 0.5, startAngle: -.pi * 0.5, endAngle: 0.0, clockwise: true)
            
            // c3
            pt.x += middleRad * 1.0
            pth.addArc(withCenter: pt, radius: middleRad * 0.5, startAngle: .pi * 1.0, endAngle: 0.0, clockwise: false)
            
            // c4
            pt.x += middleRad * 1.0
            pth.addArc(withCenter: pt, radius: middleRad * 0.5, startAngle: .pi * 1.0, endAngle: .pi * 1.5, clockwise: true)
    
            // 3
            pt = CGPoint(x: bounds.maxX - cornerRad, y: bounds.minY)
            pth.addLine(to: pt)
    
            // c5
            pth.addArc(withCenter: topRightC, radius: cornerRad, startAngle: -.pi * 0.5, endAngle: 0.0, clockwise: true)
    
            // 4
            pt = CGPoint(x: bounds.maxX, y: bounds.maxY - cornerRad)
            pth.addLine(to: pt)
            
            // c6
            pth.addArc(withCenter: botRightC, radius: cornerRad, startAngle: 0.0, endAngle: .pi * 0.5, clockwise: true)
            
            // 5
            pt = CGPoint(x: bounds.minX + cornerRad, y: bounds.maxY)
            pth.addLine(to: pt)
            
            // c7
            pth.addArc(withCenter: botLeftC, radius: cornerRad, startAngle: .pi * 0.5, endAngle: .pi * 1.0, clockwise: true)
            
            pth.close()
            
            shapeLayer.path = pth.cgPath
            
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search