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.
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 configurartoolbarItems
del controlador de vista para actualizar la interfaz. Si configura la propiedadtoolbarItems
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
}
}