29 votes

iOS 11 & iPhone X: UINavigationBar de la barre d'espacement incorrect lorsqu'il est incorporé dans UITabBarController

Je rencontre un problème ennuyeux de tester le nouveau iOS 11 sur l'iPhone X simulateur.

J'ai un UITabBarController et à l'intérieur de chaque onglet il y a un UINavigationControllerchaque UINavigationBar a défini également une barre d'outils en bas (setToolbarHidden:), et par défaut, elles s'affichent en bas, juste au-dessus de la tabBar.

Il a été fonctionne bien jusqu'à présent et semble bien fonctionner aussi dans le prochain iPhone 8 et 8 modèles Plus, mais sur l'iPhone X, il existe un fossé entre la barre d'outils et la tabBar. Ma conjecture est que la barre d'outils ne sait pas qui est affiché à l'intérieur d'un tabBar et puis laisse les accommoder de l'espace en bas.

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

  • C'est à quoi il ressemble sur iPhone 8.
  • Et là est le problème sur iPhone X.

enter image description here enter image description here

4voto

greg Points 2712

J'ai déposé ce que radr://problème/34421298, qui a été fermé comme un double de radr://problème/34462371. Cependant, dans la dernière version bêta de Xcode 9.2 (9C32c) avec iOS 11.2, ce qui semble être fixe. Voici un exemple de mon application qui s'exécute dans le simulateur de chaque appareil, avec aucun changement entre les deux.

Navbar toolbar under iOS 11.1 and 11.2

Ce n'est pas vraiment une solution à votre problème, autre que celui de la patience peut le résoudre sans avoir besoin de recourir à la ruse de l'INTERFACE utilisateur. Mon hypothèse est que iOS 11.2 va sortir avant la fin de l'année, car il est nécessaire pour soutenir HomePod.

1voto

Kamil Szostakowski Points 1683

Si vous ne considérez pas les rotations, vous pouvez essayer de manipuler le calque de la barre d'outils comme un très hacky mais rapide solution de contournement.

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, la rotation de l'animation n'a pas l'air bon quand il s'agit de cette approche. Dans ce cas, l'ajout de la barre d'outils personnalisée au lieu de la norme sera une meilleure solution.

1voto

Mousavian Points 1041

iOS 11.1 et iPhone X sont libérés et ce bug/feature n'est pas encore fixée. J'ai donc mis en place cette solution de contournement. Ce code fonctionne sous iOS 9.0+.

Définissez cette classe dans votre scénario de navigation du contrôleur de classe. Il va utiliser la barre d'outils personnalisée iPhone X avec une disposition correcte des contraintes, et les chutes de retour à la langue maternelle dans d'autres appareils. La barre d'outils personnalisé est ajouté à la navigation du contrôleur de la vue à la place de votre vue-contrôleur, de faire des transitions plus en douceur.

  • Remarque importante: Vous appelez l' updateItems(animated:) manuellement après le réglage de l' toolbarItems de votre point de vue contrôleur d'interface de mise à jour. Si vous définissez toolbarItems de la propriété de la manette de navigation, vous pouvez ignorer cette étape.

Il simule toutes les indigènes de la barre d'outils de comportement (y compris la modification de la barre d'outils de la hauteur en mode portrait/paysage modes), à l'exception de push/pop animations.

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

1voto

Silmaril Points 369

Je n'ai trouvé qu'une solution: ajouter une barre d'outils directement à la vue-contrôleur

enter image description here

0voto

Marcus Points 296

Apple n'a toujours pas encore résolu ce bug dans iOS 11.2. Dérivé de Mousavian solution ici est une approche plus simple que j'ai pris.

J'ai pris cette approche parce que j'ai juste un UITableViewController lorsque ce bug se produit. Donc dans mon cas, je viens d'ajouter le code suivant ci-dessous pour mon ViewController (qui est UITableViewController) lorsque ce bug se produit.

Les avantages sont:

  • Ce correctif prend juste plus dans le cas d'un iPhone X. Pas d'effets secondaires à attendre sur les autres appareils
  • Fonctionne avec n'importe quel de transition
  • Fonctionne indépendamment de l'autre parent/enfant contrôleurs avoir des Barres d'outils ou pas
  • 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.Mettre en œuvre des 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
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X