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

扩展 UIViewPropertyAnimator?

为啥在 UIViewPropertyAnimator 中使用“unowned”

UIViewPropertyAnimator 的反弹效果

完成块内的 UIViewPropertyAnimator 状态?

NSLayoutConstraints 的 UIViewPropertyAnimator 使视图消失