guidelines development developer apple app ios cocoa-touch ios11 iphone-x

development - iOS 11 y iPhone X: el espaciado de la barra de herramientas de UINavigationBar es incorrecto cuando está incrustado en UITabBarController



ios guidelines (5)

Estoy experimentando un problema molesto al probar el nuevo iOS 11 en el simulador de iPhone X.

Tengo un UITabBarController y dentro de cada pestaña hay un UINavigationController , cada UINavigationBar ha definido también una barra de herramientas inferior ( setToolbarHidden: , y por defecto se muestran en la parte inferior, justo sobre la barra de barra.

Hasta ahora ha funcionado bien y parece funcionar bien también en los modelos de iPhone 8 y 8 Plus de próxima aparición, pero en el iPhone X hay una brecha entre la barra de herramientas y la barra de herramientas. Mi conjetura es que la barra de herramientas no se da cuenta de que se muestra dentro de una barra de pestañas y luego deja el espacio de alojamiento en la parte inferior.

Supongo que la única forma de solucionarlo sería utilizar una barra de herramientas personalizada y mostrarla / animarla yo mismo en lugar de usar la UINavigationBar predeterminada, pero me gustaría escuchar otras opciones :)

  • Así es como se ve en iPhone 8.
  • Y aquí está el problema en el iPhone X.


Apple aún no ha solucionado este error en iOS 11.2. Derivado de la solución de Mousavian, aquí hay un enfoque más simple que tomé.

Tomé este enfoque porque solo tengo un UITableViewController donde ocurre este error. Entonces, en mi caso, acabo de agregar el siguiente código que aparece a continuación a mi ViewController (que es UITableViewController) donde ocurre este error.

Las ventajas son:

  • Esta solución solo toma el control en caso de un iPhone X. No se esperan efectos secundarios en otros dispositivos
  • Funciona con cualquier transición
  • Funciona independientemente de que otros controladores padre / hijo tengan barras de herramientas o no
  • Sencillo

Y aquí está el código:

1.Agregue startFixIPhoneXToolbarBug a su vista . Aparecerá así:

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

2.Agregue endFixIPhoneXToolbarBug a su vista . Aparecerá Desaparecido así:

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

3. Implemente start / endFixIPhoneXToolbarBug en su viewController de esta manera:

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


Archivé esto como radr: // problem / 34421298, que se cerró como un duplicado de radr: // problem / 34462371. Sin embargo, en la última versión beta de Xcode 9.2 (9C32c) con iOS 11.2, esto parece estar solucionado. Aquí hay un ejemplo de mi aplicación ejecutándose en el simulador de cada dispositivo, sin cambios intermedios.

Esto no es realmente una solución a su problema, aparte de que un poco de paciencia puede resolverlo sin necesidad de recurrir al engaño de UI. Supongo que iOS 11.2 estará disponible antes de fin de año, ya que es necesario para admitir HomePod.


He encontrado una sola solución: agregar la barra de herramientas directamente al controlador de vista


Si no considera las rotaciones, puede tratar de manipular la capa de la barra de herramientas como una solución rápida, pero muy complicada.

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

Desafortunadamente, la animación de rotación no se ve bien cuando se trata de este enfoque. En este caso, agregar la barra de herramientas personalizada en lugar de la estándar será una mejor solución.


iOS 11.1 y iPhone X se lanzaron y este error / característica aún no se ha solucionado. Así que implementé esta solución. Este código funciona en iOS 9.0+.

Simplemente configura esta clase en tu guión gráfico como clase de controlador de navegación. Utilizará la barra de herramientas personalizada en el iPhone X con las restricciones de diseño correctas, y volverá a ser nativa en otros dispositivos. La barra de herramientas personalizada se agrega a la vista del controlador de navegación en lugar de su controlador de vista, para que las transiciones sean más suaves.

  • Nota importante: debe llamar a updateItems(animated:) manualmente después de configurar toolbarItems del controlador de vista para actualizar la interfaz. Si configura la propiedad toolbarItems del controlador de navegación, puede ignorar este paso.

Simula todo el comportamiento nativo de la barra de herramientas (incluido el cambio de la altura de la barra de herramientas en los modos vertical / horizontal), excepto las animaciones 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 } }