UIViewPropertyAnimator:同时隐藏 TabBar 和 StatusBar (iOS 13)
Posted
技术标签:
【中文标题】UIViewPropertyAnimator:同时隐藏 TabBar 和 StatusBar (iOS 13)【英文标题】:UIViewPropertyAnimator: Hide both TabBar and StatusBar simultaneously (iOS 13) 【发布时间】:2020-04-09 00:22:59 【问题描述】:试图同时隐藏 TabBar 和 StatusBar 并在同一个动画块内,我遇到了一个难以理解的布局行为。开始使用 tabbar item viewcontroller 以通常的方式隐藏 TabBar:
import UIKit
class TestViewController: UIViewController
var mainViewController: UITabBarController
get
return UIApplication.shared.windows.first $0.rootViewController != nil?.rootViewController as! UITabBarController
var offset: CGFloat!
override func viewDidLoad()
super.viewDidLoad()
offset = mainViewController.tabBar.frame.height
@IBAction func HideMe(_ sender: Any)
let tabBar = self.mainViewController.tabBar
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
animator.startAnimation()
到目前为止一切顺利:
现在让我们为 StatusBar 添加动画:
import UIKit
class TestViewController: UIViewController
var mainViewController: UITabBarController
get
return UIApplication.shared.windows.first $0.rootViewController != nil?.rootViewController as! UITabBarController
var isTabBarHidden = false
didSet(newValue)
setNeedsStatusBarAppearanceUpdate()
override var prefersStatusBarHidden: Bool
get
return isTabBarHidden
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation
get
return .slide
var offset: CGFloat!
override func viewDidLoad()
super.viewDidLoad()
offset = mainViewController.tabBar.frame.height
@IBAction func HideMe(_ sender: Any)
let tabBar = self.mainViewController.tabBar
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
self.isTabBarHidden = true
animator.startAnimation()
现在 StatusBar 在滑动,bur TabBar 冻结(我不知道为什么):
任何使用 layoutIfNeeded()、setNeedsLayout() 等更新布局的尝试均不成功。现在让我们交换 TabBar 和 StatusBar 的动画:
@IBAction func HideMe(_ sender: Any)
let tabBar = self.mainViewController.tabBar
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
self.isTabBarHidden = true
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
animator.startAnimation()
现在两者都在滑动,但 TabBar 在动画开始时开始跳跃:
我发现在将 StatusBar 的指令添加到动画块时,会开始另外调用 ViewDidLayoutSubviews()。其实你可以在 ViewDidLayoutSubviews() 中固定 TabBar 的初始位置:
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
if isTabBarHidden
let tabBar = self.mainViewController.tabBar
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
这种方法的缺点是TabBar在移动的过程中会抽搐,这取决于移动的速度和其他因素。
另一种方式(不使用 ViewDidLayoutSubviews())与逻辑相反,但在实践中有效。也就是说,您可以将一个动画放在另一个动画的完成块中:
@IBAction func HideMe(_ sender: Any)
let tabBar = self.mainViewController.tabBar
let animator1 = UIViewPropertyAnimator(duration: 1, curve: .linear)
self.isTabBarHidden = !self.isTabBarHidden
animator1.addCompletion(_ in
let animator2 = UIViewPropertyAnimator(duration: 1, curve: .linear)
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
animator2.startAnimation()
)
animator1.startAnimation()
按照逻辑,我们有两个连续的动画。并且 TabBar 动画应该在 StatusBar 动画结束后开始。但是,在实践中:
这种方法的缺点是,如果你想反转动画(例如,用户在 TabBar 移动时点击了屏幕),变量 animator1.isRunning 将是 false,虽然在物理上StatusBar 仍然会在屏幕上移动(我也不知道为什么)。
期待阅读您的 cmets、建议、解释。
【问题讨论】:
【参考方案1】:逻辑是 setNeedsStatusBarAppearanceUpdate() 是异步动画的。 IE。 StatusBar 动画在开始后立即结束,然后在一个不能暂停或反转的线程中运行。遗憾的是 ios SDK 没有提供 StatusBar 动画控件。 如何防止setNeedsStatusBarAppearanceUpdate()动画对布局的影响,我还是不知道。
【讨论】:
以上是关于UIViewPropertyAnimator:同时隐藏 TabBar 和 StatusBar (iOS 13)的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 UIViewPropertyAnimator 中使用“unowned”