能否自定义Tab Bar Controller?自定义后遇UINavigationController适配问题
我之前也做过带突出中间大按钮、自定义徽章和彩色标签的TabBar,完全懂你遇到的这个麻烦——系统UINavigationController默认会把视图铺满整个屏幕,不管自定义TabBar的高度和样式,导致内容被遮挡,只能手动调内边距,既不优雅也容易在不同设备上出问题。下面分享几个更靠谱的解决方案:
1. 用additionalSafeAreaInsets动态适配底部内边距
这是最推荐的方式,不用硬编码数值,自动适配自定义TabBar的额外高度:
class CustomTabBarController: UITabBarController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 计算自定义TabBar超出系统默认高度的部分(系统默认TabBar总高度=安全区底部高度+49pt) let systemTabBarTotalHeight = view.safeAreaInsets.bottom + 49 let extraTabBarHeight = tabBar.frame.height - systemTabBarTotalHeight // 给所有子控制器设置额外底部安全区内边距 for childVC in children { childVC.additionalSafeAreaInsets.bottom = extraTabBarHeight // 如果是UINavigationController,同步给它的顶层页面 if let navVC = childVC as? UINavigationController { navVC.topViewController?.additionalSafeAreaInsets.bottom = extraTabBarHeight } } } }
不管你的TabBar是因为高度增加还是中间按钮突出,子控制器(包括导航栏里的页面)都会自动调整内容位置,不会被TabBar遮挡。
2. 让中间按钮悬浮在TabBar上方,保持TabBar原高度
如果中间按钮只是视觉上突出,不需要占用内容区域的高度,可以把TabBar高度维持系统默认值,让按钮作为子视图叠加在TabBar上方:
class CustomTabBar: UITabBar { private let centerButton = UIButton(type: .custom) override init(frame: CGRect) { super.init(frame: frame) setupCenterButton() } required init?(coder: NSCoder) { super.init(coder: coder) setupCenterButton() } private func setupCenterButton() { // 设置按钮尺寸和样式,让它向上突出TabBar centerButton.frame = CGRect(x: bounds.midX - 30, y: -25, width: 60, height: 60) centerButton.backgroundColor = .systemBlue centerButton.layer.cornerRadius = 30 addSubview(centerButton) // 确保按钮在最上层,不被其他TabBarItem遮挡 bringSubviewToFront(centerButton) } override func layoutSubviews() { super.layoutSubviews() // 旋转或布局变化时重新定位按钮 centerButton.center.x = bounds.midX } }
这种方式下,TabBar的实际布局高度还是系统默认值,UINavigationController的内容会自动适配到安全区底部,完全不需要手动调内边距,视觉上也能实现按钮突出的效果。
3. 直接给UINavigationController的视图加约束
如果你不想修改安全区内边距,也可以直接约束导航控制器的视图,让它停在自定义TabBar的上方:
override func viewDidLoad() { super.viewDidLoad() for childVC in children { guard let navVC = childVC as? UINavigationController else { continue } navVC.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ navVC.view.topAnchor.constraint(equalTo: view.topAnchor), navVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), navVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), navVC.view.bottomAnchor.constraint(equalTo: tabBar.topAnchor) ]) } }
注意要确保自定义TabBar的底部约束是锚定到视图的安全区底部,不然在不同设备上可能会出现布局错位。
问题根源
系统自带的UITabBarController会根据内置TabBar的高度自动给子控制器设置正确的safeAreaInsets,但完全自定义TabBar后,系统无法识别你修改后的TabBar高度,导致UINavigationController的视图默认延伸到屏幕底部,最终被TabBar遮挡。上面的方法都是通过手动传递正确的布局边界,替代生硬的内边距调整。
内容的提问来源于stack exchange,提问作者ZaBlanc

