wake-up-neo.com

iOS 11 und iPhone X: Der Abstand der Symbolleisten von UINavigationBar ist falsch, wenn er in UITabBarController eingebettet ist

Beim Testen des neuesten iOS 11 auf dem iPhone X-Simulator tritt ein ärgerliches Problem auf.

Ich habe eine UITabBarController und in jeder Registerkarte gibt es eine UINavigationController, jede UINavigationBar hat auch eine unterste Toolbar (setToolbarHidden:) definiert, die standardmäßig über der Tableiste angezeigt wird.

Es hat bisher gut funktioniert und scheint auch bei den kommenden iPhone 8 und 8 Plus-Modellen gut zu funktionieren, aber beim iPhone X gibt es eine Lücke zwischen der Toolbar und der Tabbar. Meine Vermutung ist, dass die Werkzeugleiste nicht erkennt, dass sie in einer Tab-Leiste angezeigt wird und dann den Aufnahmeraum unten belässt.

Ich denke, der einzige Weg, das Problem zu beheben, wäre die Verwendung einer benutzerdefinierten Symbolleiste und die Anzeige/Animation selbst, anstatt die Standardeinstellungen UINavigationBar zu verwenden. Ich würde jedoch gerne andere Optionen hören :)

  • So sieht es auf dem iPhone 8 aus.
  • Und hier ist das Problem auf dem iPhone X.

 enter image description here  enter image description here

28
apascual

Ich habe dies als radr: // problem/34421298 eingereicht, das als Duplikat von radr: // problem/34462371 geschlossen wurde. In der neuesten Beta von Xcode 9.2 (9C32c) mit iOS 11.2 scheint dies jedoch behoben zu sein. Hier ist ein Beispiel für meine App, die im Simulator jedes Geräts ausgeführt wird, ohne dass dazwischen Änderungen vorgenommen werden.

 Navbar toolbar under iOS 11.1 and 11.2

Dies ist nicht wirklich eine Lösung für Ihr Problem, abgesehen von etwas Geduld kann es gelöst werden, ohne auf die Tricks der Benutzeroberfläche zurückgreifen zu müssen. Ich gehe davon aus, dass iOS 11.2 noch vor Jahresende verfügbar sein wird, da es zur Unterstützung von HomePod erforderlich ist.

4
greg

Wenn Sie keine Rotationen in Betracht ziehen, können Sie versuchen, die Ebene der Symbolleiste als sehr hackige, aber schnelle Abhilfe zu manipulieren.

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

Leider sieht Rotationsanimation bei diesem Ansatz nicht gut aus. In diesem Fall ist das Hinzufügen der benutzerdefinierten Symbolleiste anstelle der Standard-Symbolleiste eine bessere Lösung.

1

iOS 11.1 und iPhone X sind veröffentlicht und dieser Fehler/Feature ist noch nicht behoben. Also habe ich diesen Workaround implementiert. Dieser Code funktioniert in iOS 9.0 und höher.

Legen Sie diese Klasse in Ihrem Storyboard einfach als Navigations-Controller-Klasse fest. Es verwendet eine benutzerdefinierte Symbolleiste in iPhone X mit korrekten Layouteinschränkungen und greift in anderen Geräten auf die native Symbolleiste zurück. Die benutzerdefinierte Symbolleiste wird zur Ansicht des Navigationscontrollers anstelle des Viewcontrollers hinzugefügt, um Übergänge zu glätten.

  • Wichtiger Hinweis: Sie müssen updateItems(animated:) manuell aufrufen, nachdem Sie toolbarItems Ihres View-Controllers eingestellt haben, um die Schnittstelle zu aktualisieren. Wenn Sie die toolbarItems-Eigenschaft des Navigationscontrollers festlegen, können Sie diesen Schritt ignorieren.

Es simuliert alles native Werkzeugleistenverhalten (einschließlich Ändern der Höhe der Werkzeugleiste in den Hoch-/Querformat-Modi) mit Ausnahme von Push/Pop-Animationen.

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 hat diesen Fehler in iOS 11.2 noch nicht behoben. Abgeleitet von Mousavians Lösung, habe ich hier einen einfacheren Ansatz gewählt. 

Ich habe diesen Ansatz gewählt, weil ich nur einen UITableViewController habe, bei dem dieser Fehler auftritt. In meinem Fall habe ich gerade den folgenden Code zu meinem ViewController (UITableViewController) hinzugefügt, wo dieser Fehler auftritt.

Vorteile sind:

  • Dieser Fix wird nur bei einem iPhone X übernommen. Bei anderen Geräten sind keine Nebenwirkungen zu erwarten
  • Funktioniert mit jedem Übergang
  • Funktioniert unabhängig davon, ob andere übergeordnete/untergeordnete Controller über Symbolleisten verfügen oder nicht
  • Einfach

Und hier ist der Code:

1. Fügen Sie startFixIPhoneXToolbarBug zu Ihrem viewWillAppear wie folgt hinzu:

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

    startFixIPhoneXToolbarBug()
}

2. Fügen Sie endFixIPhoneXToolbarBug zu Ihrem viewWillDisappear wie folgt hinzu:

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

    endFixIPhoneXToolbarBug()
}

3.Implement start/endFixIPhoneXToolbarBug in Ihrem viewController wie folgt:

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

Ich habe nur eine Problemumgehung gefunden: Hinzufügen von Symbolleisten direkt zum Ansichtscontroller

 enter image description here

0
Silmaril