试图同时隐藏 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 在物理上仍然会在屏幕上移动(我也不知道为什么)。
期待阅读您的评论、建议和解释。