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

Swift UITabBarController隐藏动画优化:解决滚动留白问题

问题描述

我为UITabBarController编写了以下扩展,实现带动画的隐藏与显示功能。但视图滚动时,已隐藏的tabBar会留下空白区域,滚动结束后tabBar又重现,请问如何消除该空白?

extension UITabBarController {
    @objc func hideTabBarAndUpdateFrames() {
        self.view.frame = CGRect(x: self.view.frame.origin.x,
                                 y: self.view.frame.origin.y,
                                 width: self.view.frame.size.width,
                                 height: self.view.frame.height + self.tabBar.bounds.size.height)
        UIView.animate(withDuration: 0.1) {
            self.tabBar.isHidden = true
            self.tabBar.frame.origin.y = self.view.frame.maxY
        }
    }
    
    @objc func showTabBarAndUpdateFrames() {
        UIView.animate(withDuration: 0.1) {
            self.view.frame = CGRect(x: self.view.frame.origin.x,
                                     y: self.view.frame.origin.y,
                                     width: self.view.frame.size.width,
                                     height: self.view.frame.height - self.tabBar.frame.size.height)
            self.tabBar.frame.origin.y = self.view.frame.height - self.tabBar.frame.height
            self.tabBar.layoutIfNeeded()
        }
        self.tabBar.isHidden = false
    }
}
解决方案

问题根源在于直接修改view.frame无法让子控制器视图自动填满屏幕,且UITabBarController的布局会在滚动触发系统layout更新时被重置,导致tabBar重现、空白区域显现。以下是两种可靠的解决方式:

方法一:安全区域内边距+Alpha动画(简洁版)

贴合系统布局逻辑,避免自动重置问题:

extension UITabBarController {
    @objc func hideTabBarAnimated() {
        // 让子视图延伸到tabBar原区域,消除空白
        self.additionalSafeAreaInsets.bottom -= self.tabBar.bounds.height
        UIView.animate(withDuration: 0.3) {
            // 通过Alpha隐藏+移到屏幕外,避免布局突变
            self.tabBar.alpha = 0
            self.tabBar.frame.origin.y = UIScreen.main.bounds.height
        }
    }
    
    @objc func showTabBarAnimated() {
        UIView.animate(withDuration: 0.3) {
            self.tabBar.alpha = 1
            self.tabBar.frame.origin.y = UIScreen.main.bounds.height - self.tabBar.bounds.height
            // 恢复安全区域内边距
            self.additionalSafeAreaInsets.bottom += self.tabBar.bounds.height
        }
    }
}

方法二:自定义约束控制(稳定版)

通过约束替代直接修改frame,彻底避免系统布局重置:

private var tabBarBottomConstraintKey: Void?

extension UITabBarController {
    private var tabBarBottomConstraint: NSLayoutConstraint? {
        get {
            return objc_getAssociatedObject(self, &tabBarBottomConstraintKey) as? NSLayoutConstraint
        }
        set {
            objc_setAssociatedObject(self, &tabBarBottomConstraintKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    private func setupTabBarConstraints() {
        guard tabBarBottomConstraint == nil else { return }
        tabBar.translatesAutoresizingMaskIntoConstraints = false
        let bottomConstraint = tabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        NSLayoutConstraint.activate([
            bottomConstraint,
            tabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tabBar.heightAnchor.constraint(equalToConstant: tabBar.bounds.height)
        ])
        tabBarBottomConstraint = bottomConstraint
    }
    
    @objc func hideTabBarAnimated() {
        setupTabBarConstraints()
        additionalSafeAreaInsets.bottom -= tabBar.bounds.height
        UIView.animate(withDuration: 0.3) {
            self.tabBarBottomConstraint?.constant = self.tabBar.bounds.height
            self.tabBar.alpha = 0
            self.view.layoutIfNeeded()
        }
    }
    
    @objc func showTabBarAnimated() {
        setupTabBarConstraints()
        UIView.animate(withDuration: 0.3) {
            self.tabBarBottomConstraint?.constant = 0
            self.tabBar.alpha = 1
            self.additionalSafeAreaInsets.bottom += self.tabBar.bounds.height
            self.view.layoutIfNeeded()
        }
    }
}

关键注意事项

  • 禁止直接修改UITabBarController.view的frame,系统布局系统会在滚动等场景下自动重置该frame,导致布局错乱。
  • 使用additionalSafeAreaInsets可让子控制器的滚动视图自动延伸到tabBar原区域,彻底消除空白。
  • 用alpha替代isHidden,避免布局突然变化,同时配合动画更流畅,也能防止系统因isHidden重新计算布局。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:05:39