I'm looking to implement a custom UITabBarController in Swift. I have searched if there is a library or something that can be used to implement exactly same design, but couldn't find any. Most of the answers or libraries are outdated (after some changes that Apple made on NavigationBar and TabBar on iOS 15), or are not doing the exact same functionality as needed.
So, the TabBar should be with a rounded button in the center, between the button and TabBar should be some transparent space (padding). When that button is tapped it will popup a view.
The design can be seen on the image below.
Similar logic and UX has Binance app. So, when the button in the center is tapped, it popups a view to navigate to another View Controllers.
My Implementation
One of my implementations that worked best is the following one. I have two classes CustomTabBarController and CustomTabBar.
CustomTabBarController
import UIKit
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
setupMiddleButton()
}
// TabBarButton – Setup Middle Button
func setupMiddleButton() {
let middleBtn = UIButton(frame: CGRect(x: (self.view.bounds.width / 2)-25, y: -20, width: 50, height: 50))
//STYLE THE BUTTON YOUR OWN WAY
middleBtn.backgroundColor = .blue
middleBtn.layer.cornerRadius = (middleBtn.layer.frame.width / 2)
//add to the tabbar and add click event
self.tabBar.addSubview(middleBtn)
middleBtn.addTarget(self, action: #selector(self.menuButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
// 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.
print("MenuButton")
}
}
CustomTabBar
import UIKit
@IBDesignable
class CustomTabBar: UITabBar {
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) {
self.addShape()
}
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))
path.close()
return path.cgPath
}
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
}
}
Results
The results are not quite as expected. It's not creating a transparency on the curve in center. It's taking background color or sometimes adding just white background. This can be noticed mostly on TableView or any other scrolling UI
As for popup view when Floating Button is pressed I would like to hear any suggestion from you. What would be the best option for that?
Thank you in advance for your contribution.