Пытаясь скрыть и TabBar, и StatusBar одновременно и внутри одного и того же блока анимации, я натолкнулся на непонятное поведение макета. Начинаем скрывать TabBar обычным способом с viewcontroller элемента табуляции:
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 скользит, но TabBar застыл (я не знаю почему):
Любые попытки обновить макет с помощью layoutIfNeeded (), setNeedsLayout () et c. были неудачными. Теперь давайте поменяем анимацию для 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 () начинает вызываться дополнительно. На самом деле вы можете исправить начальную позицию TabBar внутри ViewDidLayoutSubviews ():
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if isTabBarHidden {
let tabBar = self.mainViewController.tabBar
tabBar.frame = tabBar.frame.offsetBy(dx: 0, dy: self.offset)
}
}
Недостаток этого метода заключается в том, что TabBar может дергаться во время движение, в зависимости от скорости движения и других факторов.
Другой способ (без использования ViewDidLayoutSubviews ()) противоречит логи c, но работает на практике. А именно, вы можете поместить одну анимацию в блок завершения другого:
@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()
}
Следуя логике c, мы имеем две последовательные анимации. А анимация TabBar должна начинаться после завершения анимации StatusBar. Однако на практике:
Недостаток этого метода заключается в том, что если вы хотите повернуть анимацию вспять (например, пользователь коснулся экрана во время TabBar перемещается), переменная animator1.isRunning будет иметь значение false, хотя физически StatusBar будет по-прежнему перемещаться по экрану (я также не знаю, почему).
Ждем чтения Ваши комментарии, предложения, объяснения.