web-dev-qa-db-fra.com

iOS 11 et iPhone X: l'espacement des barres d'outils UINavigationBar est incorrect lorsqu'il est intégré à UITabBarController

Je rencontre un problème ennuyeux lorsque je teste le dernier iOS 11 sur le simulateur iPhone X.

J'ai une UITabBarController et à l'intérieur de chaque onglet il y a une UINavigationController, chaque UINavigationBar a également défini une barre d'outils inférieure (setToolbarHidden:) et, par défaut, elles apparaissent en bas, juste au-dessus de la barre de tabulation.

Cela a bien fonctionné jusqu'à présent et semble fonctionner également dans les modèles à venir pour iPhone 8 et 8 Plus, mais sur l'iPhone X, il y a un écart entre la barre d'outils et la barre de tabulation. Mon hypothèse est que la barre d’outils ne réalise pas qu’elle s’affiche à l’intérieur d’un tabBar et qu’elle laisse ensuite l’espace de rangement en bas.

Je suppose que le seul moyen de résoudre ce problème consiste à utiliser une barre d’outils personnalisée et à l’afficher/l’animer moi-même au lieu d’utiliser les valeurs par défaut UINavigationBar, mais j’aimerais entendre d’autres options :)

  • Voilà à quoi ça ressemble sur iPhone 8.
  • Et voici le problème sur iPhone X.

 enter image description here  enter image description here

28
apascual

J'ai classé cela sous le nom radr: // problem/34421298, qui était fermé en tant que duplicata de radr: // problem/34462371. Cependant, dans la dernière version bêta de Xcode 9.2 (9C32c) avec iOS 11.2, cela semble être résolu. Voici un exemple de mon application en cours d'exécution dans le simulateur de chaque appareil, sans aucun changement entre les deux.

 Navbar toolbar under iOS 11.1 and 11.2

Ce n'est pas vraiment une solution à votre problème, mis à part le fait qu'un peu de patience peut le résoudre sans avoir besoin de recourir à la ruse de l'interface utilisateur. Mon hypothèse est qu'iOS 11.2 sera disponible avant la fin de l'année, car il est nécessaire pour prendre en charge HomePod.

4
greg

Si vous ne tenez pas compte des rotations, vous pouvez essayer de manipuler le calque de la barre d’outils comme une solution de contournement très simple mais rapide.

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

Malheureusement, l’animation par rotation n’a pas l’air bon pour cette approche. Dans ce cas, l’ajout de la barre d’outils personnalisée à la place de la barre standard sera une meilleure solution.

1
Kamil Szostakowski

iOS 11.1 et iPhone X sont publiés et ce bug/fonctionnalité n'est pas encore corrigé. J'ai donc implémenté cette solution de contournement. Ce code fonctionne dans iOS 9.0+.

Définissez simplement cette classe dans votre storyboard en tant que classe du contrôleur de navigation. Il utilisera la barre d’outils personnalisée de l’iPhone X avec des contraintes de mise en page correctes et retombera sur celle d’origine dans d’autres appareils. La barre d'outils personnalisée est ajoutée à la vue du contrôleur de navigation au lieu de votre contrôleur de vue, afin de rendre les transitions plus douces.

  • Remarque importante: Vous devez appeler updateItems(animated:) manuellement après avoir défini toolbarItems de votre contrôleur de vue pour mettre à jour l'interface. Si vous définissez la propriété toolbarItems du contrôleur de navigation, vous pouvez ignorer cette étape.

Il simule tous les comportements natifs de la barre d'outils (y compris la modification de la hauteur de la barre d'outils en mode portrait/paysage), à ​​l'exception des animations 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 n'a toujours pas corrigé ce bogue dans iOS 11.2. Dérivée de la solution de Mousavian, voici une approche plus simple que j'ai adoptée. 

J'ai choisi cette approche car je n'ai qu'un seul UITableViewController où ce bogue se produit. Donc dans mon cas, je viens d'ajouter le code suivant répertorié ci-dessous à mon ViewController (qui est UITableViewController) où ce bogue se produit.

Les avantages sont:

  • Ce correctif prend juste le dessus dans le cas d'un iPhone X. Aucun effet secondaire à attendre sur d'autres appareils
  • Fonctionne avec n'importe quelle transition
  • Fonctionne indépendamment des autres contrôleurs parent/enfant ayant des barres d'outils ou non
  • Simple

Et voici le code:

1.Ajouter startFixIPhoneXToolbarBug à votre viewWillAppear comme ceci:

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

    startFixIPhoneXToolbarBug()
}

2.Ajouter endFixIPhoneXToolbarBug à votre viewWillDisappear comme ceci:

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

    endFixIPhoneXToolbarBug()
}

3.Implement start/endFixIPhoneXToolbarBug dans votre viewController comme ceci:

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

J'ai trouvé un seul moyen de contourner le problème: ajouter une barre d'outils directement au contrôleur de vue.

 enter image description here

0
Silmaril