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
相关产品推荐
相关产品推荐

