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 :)
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.
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.
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.
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.
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
}
}
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:
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
}
}
}