iOS 11和iPhone X:embedded到UITabBarController时,UINavigationBar的工具栏间距不正确

我正在testingiPhone X模拟器上testing最新版本的iOS 11时遇到了一个烦人的问题。

我有一个UITabBarController和每个选项卡内有一个UINavigationController ,每个UINavigationBar也定义了一个底部的工具栏( setToolbarHidden: ,默认情况下,他们显示在底部,只是在tabBar。

到目前为止,它一直工作的很好,似乎也正常工作在iPhone 8和8 Plus型号,但在iPhone X之间有一个工具栏和TabBar之间的差距。 我的猜测是,工具栏并没有意识到,这是显示在一个tabBar内,然后离开的收容空间在底部。

我想解决这个问题的唯一方法是使用自定义工具栏,并自己显示/animation,而不是使用默认值UINavigationBar ,但我想听听其他选项:)

  • 这就是它在iPhone 8上的样子。
  • 这是iPhone X上的问题。

在这里输入图像说明 在这里输入图像说明

我把这个文件作为radr:// problem / 34421298提交,作为radr:// problem / 34462371的副本closures。 但是,在iOS 11.2的Xcode 9.2(9C32c)的最新testing版中,这似乎是固定的。 这里是我的应用程序在每个设备的模拟器中运行的示例,两者之间没有任何变化。

在iOS 11.1和11.2下的Navbar工具栏

这不是真的解决你的问题,除了有些耐心可以解决它,而不需要诉诸UI的诡计。 我的假设是,iOS 11.2将在今年年底之前,因为它需要支持HomePod。

如果你不考虑旋转,你可以尝试操纵工具栏的图层作为一个非常hacky但快速的解决方法。

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

不幸的是,旋转animation在这个方法上看起来不太好。 在这种情况下,添加自定义工具栏而不是标准工具栏将是更好的解决scheme。

iOS 11.1和iPhone X发布,这个bug /function尚未修复。 所以我实施了这个解决方法。 此代码适用于iOS 9.0+。

只需在导航控制器类的故事板中设置此类。 它将在iPhone X中使用具有正确布局约束的自定义工具栏,并在其他设备中回退到原生工具栏。 自定义工具栏添加到导航控制器的视图,而不是您的视图控制器,使转换更平滑。

  • 重要提示:您必须在设置视图控制器的toolbarItems以更新界面后手动调用updateItems(animated:) 。 如果您设置导航控制器的toolbarItems属性,则可以忽略此步骤。

它模拟所有本地工具栏的行为(包括改变纵向/横向模式下的工具栏高度),除了推/拉animation。

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

我发现只有一个解决方法:直接添加工具栏到视图控制器

在这里输入图像说明

苹果在iOS 11.2中仍然没有解决这个bug。 派生自Mousavian的解决scheme,这是我采取的一个更简单的方法。

我采取了这种方法,因为我只有一个UITableViewController发生此错误。 所以在我的情况下,我只是将下面列出的代码添加到我的ViewController(这是UITableViewController)发生此错误。

优点是:

  • 这个修复只是在iPhone X的情况下接pipe。在其他设备上没有任何副作用
  • 适用于任何转换
  • 无论其他父母/子控制器是否有工具栏,都可以工作
  • 简单

这里是代码:

1.添加startFixIPhoneXToolbarBug到你的viewWillAppear像这样:

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

2.添加endFixIPhoneXToolbarBug到你的viewWillDisappear像这样:

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

3.在你的viewController中像这样实现启动/结束 修复IPhoneXToolbarBug

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