如何为UITabBar设置圆角和阴影?

6

我想为 UITabBar 设置圆角和阴影,但是遇到了问题。

这是我的代码:

tabBar.barTintColor = .white
tabBar.isTranslucent = false

tabBar.layer.shadowOffset = CGSize(width: 0, height: 5)
tabBar.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
tabBar.layer.shadowOpacity = 1;
tabBar.layer.shadowRadius = 25;

tabBar.layer.masksToBounds = false
tabBar.isTranslucent = true
tabBar.barStyle = .blackOpaque
tabBar.layer.cornerRadius = 13
tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

如果我将 tabBar.layer.masksToBounds = false 改为 true,那么圆角将显示出来,但阴影则不会显示。

你找到解决方法了吗? - PvDev
3个回答

4

Swift 5

尝试这个方法。我通过在选项卡栏后面放置自定义视图来实现它。这适用于Swift 5

import UIKit

class MainTabBarController:
    UITabBarController,
    UITabBarControllerDelegate {

    let customTabBarView: UIView = {

        let view = UIView(frame: .zero)

        view.backgroundColor = .white
        view.layer.cornerRadius = 20
        view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        view.clipsToBounds = true

        view.layer.masksToBounds = false
        view.layer.shadowColor = UIColor.black.cgColor
        view.layer.shadowOffset = CGSize(width: 0, height: -8.0)
        view.layer.shadowOpacity = 0.12
        view.layer.shadowRadius = 10.0
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self

        addCustomTabBarView()
        hideTabBarBorder()
        setupTabBar()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        customTabBarView.frame = tabBar.frame
    }

    override func viewDidAppear(_ animated: Bool) {
        var newSafeArea = UIEdgeInsets()

        newSafeArea.bottom += customTabBarView.bounds.size.height
        self.children.forEach({$0.additionalSafeAreaInsets = newSafeArea})
    }

    private func addCustomTabBarView() {
        customTabBarView.frame = tabBar.frame
        view.addSubview(customTabBarView)
        view.bringSubviewToFront(self.tabBar)
    }

    func hideTabBarBorder()  {
        let tabBar = self.tabBar
        tabBar.backgroundImage = UIImage.from(color: .clear)
        tabBar.shadowImage = UIImage()
        tabBar.clipsToBounds = true
    }

    func setupTabBar() {
        self.setViewControllers([tab1, tab2, tab3], animated: false)
        self.viewDidLayoutSubviews()
    }
}

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!
    }
}

2
我找到了一种方法来实现这个功能,通过为选项卡栏添加一个单独的阴影层:

我想出了一种方法,通过为选项卡栏添加一个单独的阴影层来实现这一点:

    tabBar.clipsToBounds = true
    tabBar.layer.cornerRadius = Siz_TabBar_CornerRadius

    // For some reason you have to use the vertically opposite corners for this to show up on iPad
    tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

    shadowLayer = CALayer()
    shadowLayer.frame = tabBar.frame
    shadowLayer.backgroundColor = UIColor.clear.cgColor
    shadowLayer.cornerRadius = yourTabBarCornerRadius
    shadowLayer.shadowColor = yourShadowColor.cgColor
    shadowLayer.shadowRadius = yourShadowRadius
    shadowLayer.shadowOpacity = 1.0
    shadowLayer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]

    // This is important so the shadow doesn't lag content
    // which is scrolling underneath it.  You should tell the tab
    // bar layer to rasterize as well, the rounded corners can cause
    // performance issues with animated content underneath them.
    shadowLayer.shouldRasterize = true
    shadowLayer.rasterizationScale = UIScreen.main.scale

    // The shadow path is needed because a shadow won't
    // display for a layer with a clear backgroundColor
    let shadowPath = UIBezierPath(roundedRect: shadowLayer.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: yourTabBarCornerRadius, height: yourTabBarCornerRadius))

    // The mask makes it so that the shadow doesn't draw on
    // top of the tab bar, filling in the whole layer
    let maskLayer = CAShapeLayer()
    let maskPath = CGMutablePath()

    // This path goes around the outside of the possible shadow radius, so that the
    // shadow is between this path and the tap bar
    maskPath.addRect(CGRect(x: -yourShadowRadius, y: -yourShadowRadius, width: shadowLayer.frame.width + yourShadowRadius, height: shadowLayer.frame.height + yourShadowRadius))

    // The shadow path (shape of the tab bar) is drawn on the inside
    maskPath.addPath(shadowPath.cgPath)
    maskLayer.path = maskPath

    // This makes it so that the only shadow layer content that will
    // be drawn is located in between the two above paths
    maskLayer.fillRule = .evenOdd
    shadowLayer.mask = maskLayer

    // View here is the tab bar controller's view
    view.layer.addSublayer(shadowLayer)

2

您是正确的,将 masksToBounds 设置为 true 是允许 tabBar.layer.cornerRadius 属性生效的唯一方法,但设置为 true 将会移除任何阴影效果!

很多解决方案都涉及在选项卡栏后面添加一个虚拟视图,并对其应用阴影。这种方法可以实现效果,但一旦我尝试将虚拟视图作为父视图的子视图添加到选项卡栏后面时,当我尝试通过推送 hidesBottomBarWhenPushed=true 的视图控制器导航到另一个屏幕时,选项卡栏会隐藏,但虚拟视图仍然停留在屏幕上 :(

经过两天的反复尝试,我找到了一种不需要添加虚拟视图的解决方案,而是添加一个子层来圆角和设置投影阴影效果。这个方法非常简单,不涉及任何子视图,也不需要将 masksToBounds 属性设置为 true。

受这个答案的启发:https://dev59.com/bEoGtIcB2Jgan1znIyBU#63793084

我没有对 UITabBar 进行子类化,而是创建了一个 UITabBarController 的子类,并添加了这个函数:

private func addShape() {
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = UIBezierPath(
        roundedRect: tabBar.bounds,
        byRoundingCorners: [.topLeft, .topRight],
        cornerRadii: CGSize(width: tabBarCornerRadius, height: 0.0)).cgPath
    shapeLayer.fillColor = UIColor.white.cgColor
    shapeLayer.shadowPath =  UIBezierPath(roundedRect: tabBar.bounds, cornerRadius: tabBarCornerRadius).cgPath
    shapeLayer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2).cgColor
    shapeLayer.shadowOpacity = 1
    shapeLayer.shadowRadius = 16
    shapeLayer.shadowOffset = CGSize(width: 0, height: -6)

    // To improve rounded corner and shadow performance tremendously
    shapeLayer.shouldRasterize = true
    shapeLayer.rasterizationScale = UIScreen.main.scale

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

在viewDidLayoutSubviews的重写中调用此函数:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    addShape()
}

最后,拼图的最后一部分,选项卡栏会自动创建一个没有圆角的背景视图,因此我们需要使背景视图透明,在viewDidLoad中添加这两行代码即可:
```swift tabBar.backgroundImage = UIImage() tabBar.shadowImage = UIImage() ```
override func viewDidLoad() {
    super.viewDidLoad()
    tabBar.shadowImage = UIImage() // this removes the top line of the tabBar
    tabBar.backgroundImage = UIImage() // this changes the UI backdrop view of tabBar to transparent
}

请注意,为了使其正常工作,UITabbar 上的 translucent 属性应该为 true - hotdogsoup.nl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接