it-swarm.com.ru

iOS 11 и iPhone X: неверное расстояние между панелями инструментов UINavigationBar при встраивании в UITabBarController

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

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

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

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

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

 enter image description here  enter image description here

28
apascual

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

 Navbar toolbar under iOS 11.1 and 11.2

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

4
greg

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

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
    }    
}

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

1
Kamil Szostakowski

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's 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
    }
}
1
Mousavian

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

Я выбрал этот подход, потому что у меня есть только один 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.Implement 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
        }
    }
}
0
Marcus

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

 enter image description here

0
Silmaril