I have a tabBarController
with a curved tabBar
via caShapeLayer
.
My issue is the rectangular frame tabBar
clips the bottom of children viewControllers
.
My goal/hope was to go under and clip the bottom by the curved frame. See image for reference.
Any guidance would be really appreciated.
Code for Curved TabBar in TabBarController
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
self.tabBar.backgroundColor = .clear
self.tabBar.barStyle = .default
self.tabBar.isTranslucent = false
self.tabBar.clipsToBounds = false
hideTabBarBorder()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
addTabBarShape()
}
func addTabBarShape() {
let shapeLayer = CAShapeLayer()
let offset:CGFloat = tabBar.bounds.width/3.5
let bounds: CGRect = tabBar.bounds
let rectBounds: CGRect = CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height / 2), width: bounds.size.width, height: bounds.size.height / 2)
let rectPath: UIBezierPath = UIBezierPath(rect: rectBounds)
let ovalBounds: CGRect = CGRect(x: bounds.origin.x - (offset / 2), y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height)
let ovalPath: UIBezierPath = UIBezierPath(ovalIn: ovalBounds)
rectPath.append(ovalPath)
shapeLayer.path = rectPath.cgPath
shapeLayer.fillColor = .blue
shapeLayer.shadowPath = rectPath.cgPath
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOpacity = 0.1
shapeLayer.shadowRadius = 20
shapeLayer.shadowOffset = CGSize(width: 0, height: -8)
shapeLayer.shouldRasterize = true
shapeLayer.rasterizationScale = UIScreen.main.scale
if let oldShapeLayer = self.tabBarShapeLayer {
self.tabBar.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
} else {
self.tabBar.layer.insertSublayer(shapeLayer, at: 0)
}
self.tabBarShapeLayer = shapeLayer
}
func hideTabBarBorder() {
self.tabBar.backgroundImage = UIImage.from(color: Constants.style.darkBlue)
self.tabBar.shadowImage = UIImage()
}
extension UIImage {
static func from(color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}
3
Answers
My workaround was drawing the curve at a higher y-point than the tabBar frame...
This is because the child ViewController did not update its constraint, and it is referring to the same tabcontroller constraint. You just need to review the bottom constraint from the controller. I am using snapkit here to fix the constraint before the view appears. You can set the controller bottom margin equal to the superView and handle the tabbar offset inside your controller.
This will basically ignore the presence of the tabbar at the bottom.
You have to set the
backgroundImage
to something not visible to make the view appear to be clipped by the tabbar.I am not sure why you are explicitly setting it to
UIImage.from(color: Constants.style.darkBlue)
in your example.The following + copying all your other code is working just fine for me:
Also make sure that you connect the bottom anchor of the view (e.g. a
UITableView
) not toview.safeAreaLayoutGuide.bottomAnchor
(like you normally would do) but rather toview.bottomAnchor
to make it appear clipped by the tabBar.(changed some colors in that implementation but here you can see how it clips a
UITableView
)