在UIPageViewController的页面之间切换时避免iOS状态栏抖动

时间:2018-12-16 17:42:21

标签: ios uipageviewcontroller

我正在构建一个简单的应用,该应用使用两个页面的 UIPageViewController 。在一页上,我想显示状态栏,但在另一页上,我不想显示状态栏。为了使过渡看起来不错,我在用户完全到达 UIPageViewController 的页面后,根据其所在的页面,来回滑动状态栏。

这在像iPhone XR这样带有传感器外壳(槽口)的设备上可以很好地工作,但是在像iPhone 8这样的具有普通矩形屏幕的设备上看起来很不正常。

iPhone XR示例导航栏的高度及其下方的内容保持一致。

iPhone XR example GIF

iPhone 8示例当状态栏开始动画显示(滑动)时,内容会跳起来,但是当导航栏动画降低时,内容会返回到其原始位置。

iPhone 8 example GIF


最佳解决方案?

似乎最好的解决方案之一是以某种方式强制导航栏始终为导航栏的全高+状态栏的高度,因此,在状态栏动画化时,导航栏和内容位于同一位置下。这类似于iPhone XR屏幕上的行为。我该如何实现?


现有代码

在我的 UIPageViewController 上,我跟踪 currentViewController 属性,并相应地更新了覆盖的 prefersStatusBarHidden 变量。

private var currentViewController: UIViewController?

override var prefersStatusBarHidden: Bool {
    if let current = currentViewController,
        current == settingsNavigationController {
        return false
    } else {
        return true
    }
}

要设置 currentViewController 变量并调用必要的 setNeedsStatusBarAppearanceUpdate()方法,我将覆盖 UIPageViewControllerDelegate didFinishAnimating 方法。 strong>:

extension PageViewController: UIPageViewControllerDelegate {

    func pageViewController(_ pageViewController: UIPageViewController,
                            didFinishAnimating finished: Bool,
                            previousViewControllers: [UIViewController],
                            transitionCompleted completed: Bool) {

        currentViewController = pageViewController.viewControllers?.first

        UIView.animate(withDuration: 0.2) { () -> Void in
            self.setNeedsStatusBarAppearanceUpdate()
        }
    }
}

为了减少震撼,我使用了 .slide 动画(如果我将其保留下来,那么就没有动画了,它在矩形屏幕上还是跳动的):

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .slide
}

更新:

由于 Leon Deriglazov 的回答,我能够通过对UINavigationController进行子类化来避免内容的跳跃性。我添加了以下代码来触发动画(与状态栏动画的持续时间相同),该动画在状态栏向下滑动时向上移动内容,从而使内容看上去停留在同一位置:

class SettingsNavigationController: UINavigationController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        UIView.animate(withDuration: 0.2) {
            self.additionalSafeAreaInsets.top = 0
        }
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        additionalSafeAreaInsets.top = 20
    }
}

注意:它不包含检测设备类型的必要附加逻辑。

是这样的:

fixed jitter

尽管我希望避免像状态那样使导航栏与状态栏一起变暗,但这种感觉要流畅得多。如果有人对如何将导航栏固定在同一位置有任何想法,我仍然会感激不尽。

1 个答案:

答案 0 :(得分:2)

我会考虑在您的“设置”控制器中使用additionalSafeAreaInsets,使其始终考虑状态栏的高度。然后,当您显示状态栏时(可能不是100%不确定),您可能不得不将其关闭。