在tabBarController(_:didSelect:)中调用popToRootViewController无动画问题
问题分析与解决方案
为什么直接调用popToRootViewController没有动画?
在tabBarController(_:didSelect:)回调触发时,UITabBarController内部正处于选中状态更新的处理流程中——包括TabBar按钮的高亮动画、视图层级调整等UI操作。这些操作会占用当前RunLoop的UI更新周期,UIKit会暂时抑制新的动画请求,避免同一时间多个动画冲突,导致你的pop动画被直接跳过。
你用DispatchQueue.main.async无效,是因为它只是把任务放到当前RunLoop的下一个迭代,但TabBar的内部处理可能还没完全结束;而Timer延迟1秒是等TabBar的所有操作彻底完成,所以动画能正常执行。
可行解决方案
方案1:极短延迟的异步执行
不需要1秒这么久的延迟,只需要给TabBar的内部流程留一点收尾时间,用DispatchQueue.main.asyncAfter设置0.1秒左右的延迟即可:
class MyTabBarController: UITabBarController { func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { // 确认点击的是当前已选中的标签页 guard let currentNav = viewController as? UINavigationController, currentNav == tabBarController.selectedViewController else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { currentNav.popToRootViewController(animated: true) } } }
方案2:利用RunLoop空闲周期执行
更精准的方式是让pop操作等当前RunLoop所有pending的UI操作(包括TabBar的选中处理)完成后再执行,用perform(_:with:afterDelay:)实现:
class MyTabBarController: UITabBarController { func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { guard let currentNav = viewController as? UINavigationController, currentNav == tabBarController.selectedViewController else { return } // afterDelay:0 会将任务放到下一个RunLoop周期执行 currentNav.perform(#selector(UINavigationController.popToRootViewController(animated:)), with: true, afterDelay: 0) } }
这个方案的延迟几乎可以忽略,但能确保TabBar的内部操作全部完成,动画正常生效。
内容的提问来源于stack exchange,提问作者Calebmer
相关产品推荐
相关产品推荐

