UIViewController In-Call Статус строки Проблема

Выпуск:

Модально представленный контроллер представления не перемещается назад после исчезновения строки состояния в вызове, оставляя 20 пикселей пустым / прозрачным пространством вверху.


Нормальный: нет проблем

введите описание изображения здесь


In-Call: нет проблем

введите описание изображения здесь


После исчезновения In-Call:

Оставляет пустое / прозрачное пространство высотой 20px сверху, показывая оранжевый вид снизу. Однако строка состояния все еще присутствует над прозрачной областью. Панель навигации также оставляет место для строки состояния, ее "всего на 20 пикселей меньше места".

введите описание изображения здесь

введите описание изображения здесь


  • iOS 10 на основе
  • Модально представленный вид контроллера
  • Пользовательская модальная презентация
  • Контроллер основного вида сзади оранжевый
  • Не используется Autolayout
  • При повороте в альбомную ориентацию полоса входного вызова в 20px оставляет и все еще оставляет зазор 20px.
  • Я отказываюсь показывать строку состояния в альбомной ориентации. (т.е. большинство стандартных приложений)

Я пытался слушать делегатов приложения:

willChangeStatusBarFrame
didChangeStatusBarFrame

Также Просмотр уведомлений на основе контроллера:

UIApplicationWillChangeStatusBarFrame
UIApplicationDidChangeStatusBarFrame

Когда я записываю фрейм представленного представления для всех четырех вышеописанных методов, фрейм всегда имеет (y: 0) начало координат.


Обновить

Пользовательская модальная презентация View Controller

    let storyboard = UIStoryboard(name: "StoryBoard1", bundle: nil)
    self.modalVC = storyboard.instantiateViewController(withIdentifier: "My Modal View Controller") as? MyModalViewController
    self.modalVC!.transitioningDelegate = self
    self.modalVC.modalPresentationStyle = .custom
    self.modalVC.modalPresentationCapturesStatusBarAppearance = true;
    self.present(self.modalVC!, animated: true, completion: nil)


    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView
        let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
        toViewController!.view.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.0, options: [.curveEaseOut], animations: { () -> Void in

            toViewController!.view.transform = CGAffineTransform.identity

        }, completion: { (completed) -> Void in

           transitionContext.completeTransition(completed)

        })
 }

6 ответов

Я искал решение в течение 3 дней. Мне не нравится это решение, но я не нашел лучшего способа как это исправить.

У меня возникает ситуация, когда представление rootViewController имеет большую высоту на 20 точек, чем окно, когда я получаю уведомление об обновлениях высоты строки состояния, я вручную устанавливаю правильное значение.

Добавить метод в AppDelegate.swift

func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect) {
        if let window = application.keyWindow {
            window.rootViewController?.view.frame = window.frame
        }
    }

После этого он работает как положено (даже после изменения ориентации). Надеюсь, это кому-нибудь поможет, потому что я потратил слишком много времени на это.

PS Немного мигает, но работает.

Я тоже столкнулся с этой проблемой, но после того, как я применил этот метод, проблема исчезла.

У iOS есть метод по умолчанию willChangeStatusBarFrame для обработки строки состояния. Пожалуйста, поставьте этот метод и проверьте его.

func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
    UIView.animate(withDuration: 0.35, animations: {() -> Void in
        let windowFrame: CGRect? = ((window?.rootViewController? as? UITabBarController)?.viewControllers[0] as? UINavigationController)?.view?.frame
        if newStatusBarFrame.size.height > 20 {
            windowFrame?.origin?.y = newStatusBarFrame.size.height - 20
            // old status bar frame is 20
        }
        else {
            windowFrame?.origin?.y = 0.0
        }
        ((window?.rootViewController? as? UITabBarController)?.viewControllers[0] as? UINavigationController)?.view?.frame = windowFrame
    })
}

Надеюсь, что это поможет вам.
Спасибо

Я думаю, что это ошибка в UIKit. containerView который содержит представленное представление контроллера, которое было представлено с использованием пользовательского перехода, похоже, не полностью сдвигается назад, когда строка состояния возвращается к нормальному размеру. (Вы можете проверить иерархию представлений после закрытия строки состояния вызова)

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

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return PresentationController(presentedViewController: presented, presenting: presenting)
}

class PresentationController: UIPresentationController {
    override var shouldRemovePresentersView: Bool {
        return true
    }
}

или если вам нужно сохранить представление текущего контроллера, вы можете наблюдать за изменением фрейма строки состояния и вручную настраивать containerView быть такого же размера, как его суперпредставление

class PresentationController: UIPresentationController {
    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.onStatusBarChanged),
                                               name: .UIApplicationWillChangeStatusBarFrame,
                                               object: nil)
    }

    @objc func onStatusBarChanged(note: NSNotification) {
        //I can't find a way to ask the system for the values of these constants, maybe you can
        if UIApplication.shared.statusBarFrame.height <= 20,
            let superView = containerView?.superview {
            UIView.animate(withDuration: 0.4, animations: {
                self.containerView?.frame = superView.bounds
            })
        }
    }
}

У меня была та же проблема с личным хосотом, изменяющим строку состояния. Решение состоит в том, чтобы зарегистрироваться в системном уведомлении об изменении фрейма строки состояния, это позволит вам обновить макет и устранить все проблемы с макетом, которые могут у вас возникнуть. Мое решение, которое должно работать точно так же для вас:

  • На ваш взгляд контроллер, в viewWillAppear подозревать UIApplicationDidChangeStatusBarFrameNotification

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(myControllerName.handleFrameResize(_:)), name: UIApplicationDidChangeStatusBarFrameNotification, object: nil)
    
  • Создайте свой метод выбора

    func handleFrameResize(notification: NSNotification) {
    self.view.layoutIfNeeded() }
    
  • Удалить ваш контроллер из центра уведомлений в viewWillDisappear

        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIApplicationDidChangeStatusBarFrameNotification, object: nil)
    
  • Вам также нужно, чтобы ваш модал отвечал за строку состояния, поэтому вы должны установить

    destVC.modalPresentationCapturesStatusBarAppearance = true прежде чем представлять вид.

Вы можете реализовать это на каждом контроллере, который может иметь изменения в строке состояния, или вы можете создать другой класс, который будет делать это для каждого контроллера, например, передавать self методу, сохранять ссылку для изменения макета и иметь метод удалить себя. Вы знаете, чтобы повторно использовать код.

Я искал решение этой проблемы. На самом деле я разместил новый вопрос, похожий на этот. Здесь: Как избежать iOS Blue Location NavigationBar испортить мой StatusBar?

Поверьте, я решаю эту проблему уже пару дней, и действительно раздражает, что ваш экран испорчен из-за изменений строки состояния iOS по входящим вызовам, точкам доступа и местоположению.

Я попытался реализовать ответ Моди, я поместил этот кусок кода в свой AppDelegate и немного его изменил, но безуспешно. и я считаю, что iOS делает это автоматически, так что вам не придется реализовывать это самостоятельно.

Прежде чем я обнаружил виновника проблемы, я попробовал каждое решение в этом конкретном вопросе. Нет необходимости реализовывать метод AppDelegate willChangeStatusBar... или добавьте уведомление, чтобы наблюдать изменения статуса Bar.

Я также переделал некоторые потоки моего проекта, выполнив некоторые экраны программно (я использую раскадровки). И я немного поэкспериментировал, затем проверил мои предыдущие и другие текущие проекты, почему они делают корректировку правильно:)

Итог: я представляю свой главный экран с UITabBarController таким неправильным образом.

Пожалуйста, всегда принимайте к сведению modalPresentationStyle, У меня появилась идея проверить мой код из-за комментария Ноя.

Образец:

func presentDashboard() {
    if let tabBarController = R.storyboard.root.baseTabBarController() {
        tabBarController.selectedIndex = 1
        tabBarController.modalPresentationStyle = .fullScreen
        tabBarController.modalTransitionStyle = .crossDissolve

        self.baseTabBarController = tabBarController
        self.navigationController?.present(tabBarController, animated: true, completion: nil)
    }
}

В моем случае я использую собственный стиль представления для своего ViewController. Проблема в том, что позиция Y не рассчитана должным образом. Допустим, исходная высота экрана 736 пикселей. Попробуйте распечататьview.frame.origin.y а также view.frame.height, вы обнаружите, что высота равна 716p, а y - 20. Но высота дисплея составляет 736 - 20(дополнительная высота строки состояния во время вызова) - 20(положение y). Вот почему наше представление вырезано снизу ViewController и почему сверху есть поле 20p. Но если вы вернетесь, чтобы увидеть значение кадра контроллера навигации. Вы обнаружите, что независимо от того, отображается строка состояния во время вызова или нет, позиция y всегда равна 0.

Итак, все, что нам нужно сделать, это установить позицию y на ноль.

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

    let f = self.view.frame

    if f.origin.y != 0 {
        self.view.frame = CGRect(x: f.origin.x, y: 0, width: f.width, height: f.height)
        self.view.layoutIfNeeded()
        self.view.updateConstraintsIfNeeded()
    }
}

Я решаю эту проблему, используя одну строку кода

В цели C

tabBar.autoresizingMask = (UIViewAutoResizingFlexibleWidth | UIViewAutoResizingFlexibleTopMargin);

В Свифте

self.tabBarController?.tabBar.autoresizingMask = 
  UIViewAutoresizing(rawValue: UIViewAutoresizing.RawValue(UInt8(UIViewAutoresizing.flexibleWidth.rawValue) | UInt8(UIViewAutoresizing.flexibleTopMargin.rawValue)))`

Вам просто нужно сделать autoresizingMask из tabBar гибким сверху.

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

containerView.addSubview(toViewController.view)
toViewController.view.frame = containerView.bounds
Другие вопросы по тегам