Using iOS14.2, Swift5.3.1, Xcode 12.2,
Following this link, I achieved a custom TabBar that has a new shape.
The shape is given by a CAShapeLayer and its corresponding BezierPath (see code example below).
However, I would like to blur this custom TabBar, keeping the BezierPath-shape as its mask and at the same time, be blurry-transparent.
See the attached video at the very bottom as illustration of what I mean.
Here is the code that does the custom TabBar shape (and this part works very well)
import UIKit
@IBDesignable
class CustomTabBar: UITabBar {
private var shapeLayer: CALayer?
var scanTabShown = true
override func draw(_ rect: CGRect) {
self.addShape()
}
private func addShape() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = self.createPath()
shapeLayer.strokeColor = UIColor.lightGray.cgColor
shapeLayer.fillColor = #colorLiteral(red: 0.9782002568, green: 0.9782230258, blue: 0.9782107472, alpha: 0.75)
shapeLayer.lineWidth = 0.5
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
}
func createPath() -> CGPath {
let height: CGFloat = 72.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
if scanTabShown {
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: (centerWidth - 31 ), y: 0))
path.addCurve(to: CGPoint(x: centerWidth, y: height - 40),
controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 29, y: height - 40))
path.addCurve(to: CGPoint(x: (centerWidth + 31 ), y: 0),
controlPoint1: CGPoint(x: centerWidth + 29, y: height - 40), 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()
} else {
path.move(to: CGPoint(x: 0, 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
}
}
extension UITabBar {
override open func sizeThatFits(_ size: CGSize) -> CGSize {
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = 83
return sizeThatFits
}
}
To achieve the masked BlurView, I followed this link.
Here is my code that supposedly should add the blur effect to this custom shaped TabBar.
However the code still does not work. What is wrong ?
Inside the addShape()
method, I added the following code in order to get the desired effect. But again, it does not work. Why ???
let maskLayer = CAShapeLayer()
maskLayer.path = shapeLayer.path
maskLayer.fillRule = .evenOdd
let maskView = UIView(frame: self.frame)
maskView.backgroundColor = UIColor.black
maskView.layer.mask = maskLayer
let blurEffect = UIBlurEffect(style: .regular)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = self.frame
blurEffectView.alpha = 1.0
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
blurEffectView.mask = maskView
self.layer.addSublayer(blurEffectView.layer)
Here an illustration:
Please note, the NavBar at the top shows the desired semi-transparent and blur behaviour.
The TabBar at the bottom with the BezierPath is completely white. But I would like it to be semi-transparent and blurry as well... How can I achieve this ?
I already tried to play with an alpha-value for the shapeLayer.fillColor
. The semi-transparency is working. But not the blur !!!
Another trial was to blur the custom-shaped tabBar as a subView in the tabBarController. But again, no blur !!
Here is the code of the trial:
class SessionTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let tabBar = self.tabBar as? CustomTabBar {
let maskLayer = CAShapeLayer()
maskLayer.path = tabBar.createPath()
maskLayer.fillRule = .evenOdd
let maskView = UIView(frame: tabBar.frame)
maskView.backgroundColor = UIColor.black
maskView.layer.mask = maskLayer
let blurEffect = UIBlurEffect(style: .regular)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.backgroundColor = .green
blurEffectView.tintColor = .yellow
blurEffectView.frame = tabBar.frame
blurEffectView.alpha = 1.0
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
blurEffectView.mask = maskView
tabBar.addSubview(blurEffectView)
self.view.bringSubviewToFront(tabBar)
}
}
}