setNavigationBarHidden анимация не работает должным образом на iPhone X

У меня есть код, который входит в полноэкранный режим, скрывая панель навигации UINavigationController. Я хочу плавный анимированный эффект масштабирования при входе в полноэкранный режим. Я использую setNavigationBarHidden (_: animated:). До сих пор все это работало нормально, даже на iOS 11, но на iPhone X анимация работает не очень хорошо. При скрытии анимация отсутствует, а навигационная панель просто исчезает. При отображении он анимируется, но навигационная панель отображается медленнее, чем уменьшается область содержимого контроллера навигации, поэтому во время анимации через область навигационной панели отображается уродливый черный фон.

Я могу воссоздать это в простом тестовом приложении. У меня есть UIViewController, встроенный в UINavigationController.

Раскадровка

  • Панель навигации UINavigationController: Стиль == Черный; Прозрачный OFF
  • UIViewController: Расширить края: все опции выключены.

Я перепробовал все комбинации "Настроить вставки вида прокрутки" и "Продлить края", которые я могу придумать, но они не имели значения.

Код

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

    setFullScreen(on: fullScreen, animated: animated)
}

override var prefersStatusBarHidden: Bool
{
    return fullScreen
}

override var preferredStatusBarStyle: UIStatusBarStyle
{
    return .lightContent
}

@IBAction func onToggleNavBarVisibility(_ sender: Any) {

    if let navBarHidden = self.navigationController?.isNavigationBarHidden {
        // Toggle the state
        fullScreen = !navBarHidden

        setFullScreen(on: fullScreen, animated: true)
    }
}

private func setFullScreen(on : Bool, animated : Bool) {

    self.navigationController?.setNavigationBarHidden(on, animated: animated)
    self.setNeedsStatusBarAppearanceUpdate()
}

Результат на iPhone X (медленная анимация

2 ответа

В вашем случае вы используете оба barTintColor & navigationBarStyle с ShowHide анимация.barTintColor переопределяет значение, подразумеваемое атрибутом Style. Вы должны выбрать либо barTintColor или же navigationBarStyleВ приведенном ниже коде я только что использовал barTintColor & navigationBarStyle по умолчанию с Transulent,

    var fullScreen = false{
      didSet{
        self.setNeedsStatusBarAppearanceUpdate()
     }
   }
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Navigation Bar"
        navigationController?.navigationBar.barTintColor = .red
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        setFullScreen(on: fullScreen, animated: animated)
    }
    @IBAction func onToggleNavBarVisibility(_ sender: Any) {
        if let navBarHidden = 
          self.navigationController?.isNavigationBarHidden {
            // Toggle the state
            fullScreen = !navBarHidden
            setFullScreen(on: fullScreen, animated: true)
        }
    }
    private func setFullScreen(on : Bool, animated : Bool) {
        self.navigationController?.setNavigationBarHidden(on, animated: animated)
        self.setNeedsStatusBarAppearanceUpdate()
    }

РЕДАКТИРОВАТЬ: Если вы хотите скрыть строку состояния - используйте prefersStatusBarHidden со значением bool. & использовать setNeedsStatusBarAppearanceUpdate

   override var prefersStatusBarHidden: Bool {
        return fullScreen
    }

https://developer.apple.com/documentation/uikit/uinavigationbar

Это явно ошибка UIKit. Я подал FB8980917:

При скрытии панели навигации одновременно со строкой состояния с помощью слайд-анимации панель навигации скрывается без анимации. В противоположном направлении строка состояния отображается с анимацией затухания вместо указанной анимации слайда.

Чтобы воспроизвести, запустите прилагаемый образец проекта. Используйте медленную анимацию симулятора или запишите экран устройства и пролистайте кадры.

Я также прикрепил "Screen video.mp4" для справки.

Примечание 1. В качестве обходного пути мы могли бы прибегнуть к устаревшему API UIApplication.setStatusBarHidden(_:with:) (см. «Снимок экрана legacy.mp4»). Это в основном работает, за исключением того, что продолжительность анимации строки состояния больше, чем продолжительность анимации панели навигации. Однако для этого требуется установить UIViewControllerBasedStatusBarAppearance=NO в Info.plist, поэтому это подход «все или ничего», который отказывается от всего приложения современного API.

Примечание 2. Возврат .fade для PreferredStatusBarUpdateAnimation также не работает. Во-первых, это некрасиво, потому что панель навигации все еще выдвигается (и ее нельзя настроить так, чтобы она исчезала), во-вторых, сохраняется проблема отсутствия анимации скрытия панели навигации.

Примечание 3. Использование свойства hidesBarsOnTap UINavigationController также не работает. Проблема остается. В примере приложения также включен hidesBarsOnTap.

Образец кода:

      class ViewController: UIViewController {
    var fullScreen = false

    override var prefersStatusBarHidden: Bool {
        return navigationController!.isNavigationBarHidden
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .slide
    }

    @IBAction func toggleFullscreen(_ sender: Any) {
        fullScreen = !fullScreen

        navigationController?.setNavigationBarHidden(fullScreen, animated: true)
        setNeedsStatusBarAppearanceUpdate()
    }
}

Хотя обходной путь, описанный в примечании 1, работает, я не могу его рекомендовать, поскольку API устарел, начиная с iOS 9.0. Так что на самом деле это должны исправить люди @Apple. Тот факт, что такие приложения, как Photos, реализуют аналогичное поведение без этой ошибки, показывает, что есть способ сделать это, хотя и с помощью частного API или уродливых хаков.

Другие вопросы по тегам