Подтвердить что ты не робот

IOS 11 и iPhone X: неправильный интервал панели инструментов UINavigationBar при встраивании в UITabBarController

Я испытываю раздражающую проблему, тестирующую новейшую iOS 11 на симуляторе iPhone X.

У меня есть UITabBarController, и внутри каждой вкладки есть UINavigationController, каждый UINavigationBar также определил нижний toolBar (setToolbarHidden:), и по умолчанию они отображаются внизу, чуть выше tabBar.

Он отлично работает до сих пор и, похоже, отлично работает и в современных iPhone 8 и 8 Plus, но на iPhone X есть разрыв между toolBar и tabBar. Я предполагаю, что toolBar не понимает, что отображается внутри tabBar, а затем оставляет место для размещения внизу.

Я предполагаю, что единственный способ исправить это будет использовать пользовательскую панель инструментов и отображать/анимировать ее самостоятельно, вместо использования значений по умолчанию UINavigationBar, но я хотел бы услышать другие варианты:)

  • Вот как он выглядит на iPhone 8.
  • И вот проблема на iPhone X.

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

4b9b3361

Ответ 1

Я подал это как radr://problem/34421298, который был закрыт как дубликат radr://problem/34462371. Однако в последней бета-версии Xcode 9.2 (9C32c) с iOS 11.2 это кажется исправленным. Вот пример моего приложения, работающего в симуляторе каждого устройства, без изменений между ними.

Панель инструментов Navbar под iOS 11.1 и 11.2

На самом деле это не решение вашей проблемы, за исключением того, что некоторое терпение может решить ее, не прибегая к обману UI. Мое предположение заключается в том, что iOS 11.2 выйдет до конца года, поскольку ему необходимо поддерживать HomePod.

Ответ 2

Если вы не рассматриваете вращения, вы можете попытаться манипулировать слоем панели инструментов как очень хакерское, но быстрое решение.

class FixNavigationController: UINavigationController
{
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateTollbarPosition()
    }

    func updateTollbarPosition() {
        guard let tabbarFrame = tabBarController?.tabBar.frame else {
            return
        }
        let gapHeight = tabbarFrame.origin.y-toolbar.frame.origin.y-toolbar.frame.size.height

        var
        frame = toolbar.layer.frame
        frame.origin.y += gapHeight

        toolbar.layer.frame = frame
    }    
}

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

Ответ 3

iOS 11.1 и iPhone X выпущены, и эта ошибка/функция еще не исправлена. Поэтому я применил это решение. Этот код работает в iOS 9.0 +.

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

  • Важное примечание. Вы должны вызывать updateItems(animated:) вручную после установки toolbarItems вашего контроллера вида для обновления интерфейса. Если вы устанавливаете свойство навигационного контроллера toolbarItems, вы можете игнорировать этот шаг.

Он имитирует все поведение собственной панели (включая изменение высоты панели инструментов в портретных/ландшафтных режимах), за исключением анимации push/pop.

import UIKit

class FixNavigationController: UINavigationController {

    private weak var alterToolbarHeightConstraint: NSLayoutConstraint?

    private var _alterToolbar: UIToolbar?

    private func initAlretToolbar() {
        _alterToolbar = UIToolbar()
        _alterToolbar!.isTranslucent = true
        _alterToolbar!.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(_alterToolbar!)
        if view.traitCollection.verticalSizeClass == .compact {
            alterToolbarHeightConstraint = _alterToolbar!.heightAnchor.constraint(equalToConstant: 32.0)
        } else {
            alterToolbarHeightConstraint = _alterToolbar!.heightAnchor.constraint(equalToConstant: 44.0)
        }
        let bottomAnchor: NSLayoutConstraint
        if #available(iOS 11.0, *) {
            bottomAnchor = _alterToolbar!.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        } else {
            bottomAnchor = _alterToolbar!.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
        }
        NSLayoutConstraint.activate([
            _alterToolbar!.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            _alterToolbar!.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            bottomAnchor,
            alterToolbarHeightConstraint!
            ])
        self.view.updateFocusIfNeeded()
        self.view.layoutIfNeeded()
    }

    private var alterToolbarInSuper: UIToolbar? {
        var superNavigationController = self.navigationController as? FixNavigationController
        while superNavigationController != nil {
            if superNavigationController?._alterToolbar != nil {
                return superNavigationController?._alterToolbar
            }
            superNavigationController = superNavigationController?.navigationController as? FixNavigationController
        }
        return nil
    }

    private var alterToolbar: UIToolbar! {
        get {
            if let t = alterToolbarInSuper {
                return t
            }
            if _alterToolbar == nil {
                initAlretToolbar()
            }
            return _alterToolbar
        }
    }

    // This is the logic to determine should use custom toolbar or fallback to native one
    private var shouldUseAlterToolbar: Bool {
        // return true if height is iPhone X one
        return UIScreen.main.nativeBounds.height == 2436
    }

    /// Manually call it after setting toolbar items in child view controllers
    func updateItems(animated: Bool = false) {
        if shouldUseAlterToolbar {
            (_alterToolbar ?? alterToolbarInSuper)?.setItems(viewControllers.last?.toolbarItems ?? toolbarItems, animated: animated)
        }
    }

    override var isToolbarHidden: Bool {
        get {
            if shouldUseAlterToolbar {
                return _alterToolbar == nil && alterToolbarInSuper == nil
            } else {
                return super.isToolbarHidden
            }
        }
        set {
            if shouldUseAlterToolbar {
                if newValue {
                    super.isToolbarHidden = newValue
                    _alterToolbar?.removeFromSuperview()
                    _alterToolbar = nil
                    self.view.updateFocusIfNeeded()
                    self.view.layoutIfNeeded()
                    // TODO: Animation when push/pop
                    alterToolbarHeightConstraint = nil
                    var superNavigationController = self.navigationController as? FixNavigationController
                    while let superNC = superNavigationController {
                        if superNC._alterToolbar != nil {
                            superNC._alterToolbar?.removeFromSuperview()
                            superNC._alterToolbar = nil
                            superNC.view.updateFocusIfNeeded()
                            superNC.view.layoutIfNeeded()
                        }
                        superNavigationController = superNC.navigationController as? FixNavigationController
                    }
                } else {
                    alterToolbar.setItems(viewControllers.last?.toolbarItems ?? toolbarItems, animated: false)
                }
            } else {
                super.isToolbarHidden = newValue
            }
        }
    }

    override func setToolbarItems(_ toolbarItems: [UIBarButtonItem]?, animated: Bool) {
        super.setToolbarItems(toolbarItems, animated: animated)
        updateItems(animated: animated)
    }

    override var toolbarItems: [UIBarButtonItem]? {
        get {
            return super.toolbarItems
        }
        set {
            super.toolbarItems = newValue
            updateItems()
        }
    }

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        guard let _alterToolbar = _alterToolbar else {
            return
        }
        self.alterToolbarHeightConstraint?.isActive = false
        let height: CGFloat = (view.traitCollection.verticalSizeClass == .compact) ? 32.0 : 44.0
        let alterToolbarHeightConstraint = _alterToolbar.heightAnchor.constraint(equalToConstant: height)
        alterToolbarHeightConstraint.isActive = true
        self.alterToolbarHeightConstraint = alterToolbarHeightConstraint
    }
}

Ответ 4

Я нашел только одно обходное решение: добавьте панель инструментов непосредственно в контроллер представления

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

Ответ 5

Apple еще не исправила эту ошибку в iOS 11.2. Полученный из решения Mousavian, вот более простой подход, который я взял.

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

Преимущества:

  • Это исправление просто берет на себя в случае iPhone X. Никаких побочных эффектов ожидать на других устройствах
  • Работает с любым переходом
  • Работает независимо от других родительских/дочерних контроллеров с панелями инструментов или нет
  • Простой

И вот код:

1. Добавьте startFixIPhoneXToolbarBug к viewWillAppear следующим образом:

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

    startFixIPhoneXToolbarBug()
}

2. Добавить endFixIPhoneXToolbarBug в viewWillDisappear следующим образом:

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

    endFixIPhoneXToolbarBug()
}

3.Выполните start/endFixIPhoneXToolbarBug в viewController следующим образом:

private var alterToolbarHeightConstraint: NSLayoutConstraint? = nil
private var alterToolbar: UIToolbar? = nil

func startFixIPhoneXToolbarBug()
{
    // Check if we are running on an iPhone X
    if UIScreen.main.nativeBounds.height != 2436
    {
        return  // No
    }
    // See if we have a Toolbar
    if let tb:UIToolbar = self.navigationController?.toolbar
    {
        // See if we already added our own
        if alterToolbar == nil
        {
            // Should always be the case
            if let tbView = tb.superview
            {
                // Create a new Toolbar and apply correct constraints
                alterToolbar = UIToolbar()
                alterToolbar!.isTranslucent = true
                alterToolbar!.translatesAutoresizingMaskIntoConstraints = false
                tb.isHidden = true
                tbView.addSubview(alterToolbar!)
                if tbView.traitCollection.verticalSizeClass == .compact
                {
                    alterToolbarHeightConstraint = alterToolbar!.heightAnchor.constraint(equalToConstant: 32.0)
                }
                else
                {
                    alterToolbarHeightConstraint = alterToolbar!.heightAnchor.constraint(equalToConstant: 44.0)
                }
                let bottomAnchor: NSLayoutConstraint
                if #available(iOS 11.0, *)
                {
                    bottomAnchor = alterToolbar!.bottomAnchor.constraint(equalTo: tbView.safeAreaLayoutGuide.bottomAnchor)
                }
                else
                {
                    bottomAnchor = alterToolbar!.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
                }
                NSLayoutConstraint.activate([
                    alterToolbar!.leadingAnchor.constraint(equalTo: tbView.leadingAnchor),
                    alterToolbar!.trailingAnchor.constraint(equalTo: tbView.trailingAnchor),
                    bottomAnchor,
                    alterToolbarHeightConstraint!
                    ])
                tbView.updateFocusIfNeeded()
                tbView.layoutIfNeeded()
            }
        }
        // Add the original items to the new toolbox
        alterToolbar!.setItems(tb.items, animated: false)
    }
}

func endFixIPhoneXToolbarBug()
{
    if alterToolbar != nil
    {
        alterToolbar!.removeFromSuperview()
        alterToolbar = nil
        alterToolbarHeightConstraint = nil

        if let tb:UIToolbar = self.navigationController?.toolbar
        {
            tb.isHidden = false
        }
    }
}