0

试图同时隐藏 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 在物理上仍然会在屏幕上移动(我也不知道为什么)。

期待阅读您的评论、建议和解释。

4

1 回答 1

0

逻辑是 setNeedsStatusBarAppearanceUpdate() 是异步动画的。即StatusBar动画在启动后立即结束,然后在一个不能暂停或反转的线程中运行。可惜iOS SDK 没有提供StatusBar 动画控制。如何防止 setNeedsStatusBarAppearanceUpdate() 动画对布局的影响,我还是不知道。

于 2020-04-09T14:35:41.477 回答