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

如何实现UITabBarItem位置变更时的垂直上下移动动画

问题描述

通过修改imageInsets和titlePositionAdjustment调整UITabBarItem位置时,添加UIView过渡动画后,UITabBarItem未按预期垂直移动,反而出现从左到右的横向过渡效果。
异常效果参考:
动画从左到右移动,而非垂直移动

原有实现代码:

class TabBarViewController : UITabBarController, UITabBarControllerDelegate{
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {

        let yChanges : CGFloat = 20

        for i in 0 ... tabBarController.tabBar.items!.count - 1{
            UIView.animate(withDuration: 0.2, animations: {
                let isSelected =  (i == tabBarController.selectedIndex)
                let contentInset = UIEdgeInsets(top: isSelected ? -yChanges : 0, left: 0, bottom: isSelected ? yChanges : 0, right: 0)
                tabBarController.tabBar.items![i].imageInsets = contentInset
                tabBarController.tabBar.items![i].titlePositionAdjustment.vertical = (i == tabBarController.selectedIndex) ? -yChanges : 0
            })
        }
    }
}
问题原因

UITabBarItem的imageInsets、titlePositionAdjustment不属于UIKit公开支持的可动画属性,把这两个属性的赋值放在UIView.animate闭包内时,系统不会生成垂直位移的帧插值动画,反而会触发UITabBar内置的选中项切换默认横向过渡逻辑,最终出现不符合预期的横向移动效果。

可行解决方案

不要直接对UITabBarItem的布局属性做动画,改为操作实际承载内容展示的UITabBar内部按钮视图的可动画属性,即可实现正常的垂直移动效果:

  • 遍历tabBar的子视图,按水平坐标排序后,得到和tabBarItem索引一一对应的按钮实例(即内部的UITabBarButton)
  • 直接对按钮的transform属性做动画,该属性是UIKit明确支持隐式动画的,不会触发系统默认的横向过渡
  • 动画过程中不要修改imageInsets和titlePositionAdjustment,避免触发UITabBar默认布局刷新打断自定义动画

修正后代码

class TabBarViewController : UITabBarController, UITabBarControllerDelegate{
    // 垂直移动距离
    private let yOffset: CGFloat = 20
    // 记录上一次选中的索引,默认初始选中第0项
    private var lastSelectedIndex: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        let currentIndex = tabBarController.selectedIndex
        // 过滤出tabBar上的按钮,按x坐标排序保证和item索引顺序一致
        let tabBarButtons = tabBarController.tabBar.subviews.filter {
            String(describing: type(of: $0)).contains("UITabBarButton")
        }.sorted { $0.frame.origin.x < $1.frame.origin.x }
        
        guard tabBarButtons.count == tabBarController.tabBar.items?.count else { return }
        
        UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut) {
            // 恢复上一个选中按钮的位置
            if self.lastSelectedIndex < tabBarButtons.count {
                tabBarButtons[self.lastSelectedIndex].transform = .identity
            }
            // 当前选中按钮做垂直向上偏移
            if currentIndex < tabBarButtons.count {
                tabBarButtons[currentIndex].transform = CGAffineTransform(translationX: 0, y: -self.yOffset)
            }
        } completion: { _ in
            self.lastSelectedIndex = currentIndex
        }
    }
}

补充说明

  • 上述遍历子视图匹配UITabBarButton的方式是iOS开发常规实现方案,不涉及私有API调用,不会影响App Store审核
  • 如果需要更精细的图标、文字偏移效果,可以自定义UITabBar子类重写布局逻辑,直接调整按钮内部imageView和titleLabel的位置,再配合transform动画,可控性比修改imageInsets更高
  • 可以根据实际交互需求调整动画参数,比如添加弹簧阻尼效果让动效更自然

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:18:16