You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS纯代码实现自定义形状TabBarController问题求助

解决自定义TabBar形状不显示的问题

我来帮你搞定这个自定义TabBar没出效果的问题!咱们一步步梳理问题、修复代码:

1. 核心问题:没让TabBarController用上你的自定义TabBar

你已经写了CustomizedTabBar,但系统默认的UITabBarController还是在使用自带的UITabBar,之前重写tabBar属性没生效,大概率是写法不对。试试下面的方式替换系统TabBar:

修改你的CustomTabBarController代码:

import UIKit
class CustomTabBarController: UITabBarController {
    
    // 初始化自定义TabBar实例
    private let _customTabBar = CustomizedTabBar()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 通过KVC替换系统默认的TabBar(因为tabBar属性是只读的)
        setValue(_customTabBar, forKey: "tabBar")
        setupCenterButton()
    }
    
    func setupCenterButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
        var menuButtonFrame = menuButton.frame
        // 适配自定义TabBar的高度(64),让按钮刚好卡在凸起位置
        menuButtonFrame.origin.y = view.bounds.height - menuButtonFrame.height - (64 - 34)/2
        menuButtonFrame.origin.x = view.bounds.width/2 - menuButtonFrame.size.width/2
        menuButton.frame = menuButtonFrame
        menuButton.clipsToBounds = true
        menuButton.layer.cornerRadius = menuButtonFrame.height/2
        // 加个背景色方便测试
        menuButton.backgroundColor = .systemBlue
        view.addSubview(menuButton)
        // 把按钮放到TabBar上层,避免被遮挡
        view.bringSubviewToFront(menuButton)
    }
}

2. 完善CustomizedTabBar的形状绘制逻辑

你的CustomizedTabBar里不仅路径是空的,还没给形状图层设置填充色,这也是形状不显示的关键。咱们完善成中间凸起的样式(适配你的中间按钮):

import UIKit
class CustomizedTabBar: UITabBar {
    private var shapeLayer: CALayer?
    
    // 自定义TabBar高度,替换之前的全局扩展
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return CGSize(width: size.width, height: 64)
    }
    
    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        // 设置填充色,和TabBar背景色一致(或自定义)
        shapeLayer.fillColor = UIColor.systemBackground.cgColor
        // 可选:加边框
        shapeLayer.strokeColor = UIColor.separator.cgColor
        shapeLayer.lineWidth = 1
        
        // 替换旧图层或插入新图层
        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 path = UIBezierPath()
        let tabBarHeight = self.bounds.height
        let centerBtnWidth: CGFloat = 64
        let cornerRadius: CGFloat = 16
        
        // 从左上角开始绘制路径
        path.move(to: CGPoint(x: 0, y: 0))
        // 左半部分直线到凸起左侧圆角
        path.addLine(to: CGPoint(x: (self.bounds.width - centerBtnWidth)/2 - cornerRadius, y: 0))
        // 凸起左上角圆角
        path.addArc(withCenter: CGPoint(x: (self.bounds.width - centerBtnWidth)/2, y: cornerRadius), 
                    radius: cornerRadius, 
                    startAngle: .pi, 
                    endAngle: .pi/2, 
                    clockwise: true)
        // 凸起左侧竖线到底部圆角
        path.addLine(to: CGPoint(x: (self.bounds.width - centerBtnWidth)/2, y: tabBarHeight - cornerRadius))
        path.addArc(withCenter: CGPoint(x: (self.bounds.width - centerBtnWidth)/2 + cornerRadius, y: tabBarHeight), 
                    radius: cornerRadius, 
                    startAngle: .pi*3/2, 
                    endAngle: 0, 
                    clockwise: true)
        // 底部横线到凸起右侧圆角
        path.addLine(to: CGPoint(x: (self.bounds.width + centerBtnWidth)/2 - cornerRadius, y: tabBarHeight))
        path.addArc(withCenter: CGPoint(x: (self.bounds.width + centerBtnWidth)/2, y: tabBarHeight - cornerRadius), 
                    radius: cornerRadius, 
                    startAngle: 0, 
                    endAngle: .pi/2, 
                    clockwise: true)
        // 凸起右侧竖线到顶部圆角
        path.addLine(to: CGPoint(x: (self.bounds.width + centerBtnWidth)/2, y: cornerRadius))
        path.addArc(withCenter: CGPoint(x: (self.bounds.width + centerBtnWidth)/2 - cornerRadius, y: 0), 
                    radius: cornerRadius, 
                    startAngle: .pi/2, 
                    endAngle: 0, 
                    clockwise: true)
        // 右半部分直线到右上角,再闭合路径
        path.addLine(to: CGPoint(x: self.bounds.width, y: 0))
        path.addLine(to: CGPoint(x: self.bounds.width, y: tabBarHeight))
        path.addLine(to: CGPoint(x: 0, y: tabBarHeight))
        path.close()
        
        return path.cgPath
    }
    
    // 修复中间按钮的点击响应问题
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
        // 定义中间按钮的点击区域(超出TabBar的部分也要响应)
        let buttonHitArea = CGRect(x: (self.bounds.width - 64)/2, y: -20, width: 64, height: 64)
        if buttonHitArea.contains(point) {
            // 让父视图处理点击,确保中间按钮能响应
            return self.superview?.hitTest(convert(point, to: self.superview), with: event)
        }
        return super.hitTest(point, with: event)
    }
    
    // 布局变化时重新绘制形状(比如旋转屏幕)
    override func layoutSubviews() {
        super.layoutSubviews()
        addShape()
    }
}

3. 清理冗余代码

删掉之前写的UITabBar扩展,因为我们已经在CustomizedTabBar里重写了sizeThatFits方法。

现在运行代码,你的TabBar应该会显示中间凸起的自定义形状,中间按钮也能正常显示并响应点击了。如果需要调整形状样式,修改createPath里的路径逻辑即可。

内容的提问来源于stack exchange,提问作者asteezy24

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:30:40