iOS 11 和 iPhone X:UINavigationBar 的工具栏间距在嵌入到 UITabBarController 时不正确
Posted
技术标签:
【中文标题】iOS 11 和 iPhone X:UINavigationBar 的工具栏间距在嵌入到 UITabBarController 时不正确【英文标题】:iOS 11 & iPhone X: UINavigationBar's toolbar spacing incorrect when embedded in UITabBarController 【发布时间】:2017-09-13 19:46:41 【问题描述】:我在 iPhone X 模拟器上测试最新的 ios 11 时遇到了一个烦人的问题。
我有一个UITabBarController
,每个选项卡内都有一个UINavigationController
,每个UINavigationBar
还定义了一个底部工具栏(setToolbarHidden:
),默认情况下它们显示在底部,就在tabBar 上方.
到目前为止,它运行良好,并且在即将推出的 iPhone 8 和 8 Plus 型号中似乎也运行良好,但在 iPhone X 上,工具栏和选项卡栏之间存在差距。我的猜测是toolBar没有意识到它显示在tabBar内部,然后将容纳空间留在了底部。
我想修复它的唯一方法是使用自定义工具栏并自己显示/动画它,而不是使用默认值 UINavigationBar
,但我想听听其他选项 :)
【问题讨论】:
你找到解决这个问题的方法了吗?我遇到了类似的问题。 @AtWork 没错,我只是想指出两个空格是相似的,这意味着问题的根源是工具栏没有意识到下面有一个标签栏... 我刚刚在 Apple 的 iOS HIG 中发现了一个提示框,位于 Toolbars 页面和标签栏页面的底部。最后一句写道,“标签栏和工具栏永远不会同时出现在同一个视图中。” 鉴于这个问题的存在是因为它违反了他们的设计准则,我担心 Apple 可能不会迅速解决这个问题。我不希望 Apple 在发布时发布针对 iPhone X 的修复程序,而是尝试从我的标签栏场景中删除所有工具栏。 这似乎已在 iOS 11.2 中修复,使用 Xcode 9.2 beta (9C32c) 的模拟器。 它似乎又回到了 iOS 13(测试版)和 Xcode 11(测试版)中。我当然希望这是一个苹果的错误。我会提交一份错误报告。 【参考方案1】:我将其归档为 radr://problem/34421298,它作为 radr://problem/34462371 的副本被关闭。但是,在带有 iOS 11.2 的 Xcode 9.2 (9C32c) 的最新测试版中,这似乎已得到修复。这是我的应用在每台设备的模拟器中运行的示例,两者之间没有任何变化。
这并不是您的问题的真正解决方案,除了一些耐心可能会解决它而无需诉诸 UI 技巧。我的假设是 iOS 11.2 将在今年年底前发布,因为它需要支持 HomePod。
【讨论】:
【参考方案2】:如果您不考虑旋转,您可以尝试将工具栏的图层作为一种非常简单但快速的解决方法来操作。
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
不幸的是,当涉及到这种方法时,旋转动画看起来并不好。在这种情况下,添加自定义工具栏而不是标准工具栏将是更好的解决方案。
【讨论】:
【参考方案3】:我只找到了一种解决方法:将工具栏直接添加到视图控制器
【讨论】:
谢谢,但正如我在原始问题中所说:“我想修复它的唯一方法是使用自定义工具栏并自己显示/动画它,而不是使用默认的 UINavigationBar,但我想听听其他选择:)"【参考方案4】:iOS 11.1 和 iPhone X 已发布,但此错误/功能尚未修复。所以我实施了这个解决方法。此代码适用于 iOS 9.0+。
只需在情节提要中将此类设置为导航控制器的类即可。它将在 iPhone X 中使用具有正确布局约束的自定义工具栏,并在其他设备中回退到本机工具栏。自定义工具栏被添加到导航控制器的视图而不是您的视图控制器中,以使过渡更平滑。
重要提示:在设置视图控制器的toolbarItems
更新界面后,您必须手动调用updateItems(animated:)
。如果你设置了导航控制器的toolbarItems
属性,可以忽略这一步。
它模拟所有原生工具栏行为(包括在纵向/横向模式下更改工具栏高度),推送/弹出动画除外。
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
【讨论】:
【参考方案5】:Apple 尚未在 iOS 11.2 中修复此错误。源自 Mousavian 的解决方案,这是我采用的一种更简单的方法。
我采用这种方法是因为我只有一个 UITableViewController 会发生此错误。因此,就我而言,我只是将下面列出的以下代码添加到发生此错误的 ViewController(即 UITableViewController)中。
优点是:
此修复仅适用于 iPhone X。在其他设备上不会产生任何副作用 适用于任何过渡 无论其他父/子控制器是否有工具栏都可以工作 简单这里是代码:
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 中实现 start/endFixIPhoneXToolbarBug,如下所示:
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
【讨论】:
以上是关于iOS 11 和 iPhone X:UINavigationBar 的工具栏间距在嵌入到 UITabBarController 时不正确的主要内容,如果未能解决你的问题,请参考以下文章
iOS 11 iPhone X 模拟器 TabBar 图标和标题呈现在顶部相互覆盖
iOS 11.1 到 11.2,iPhone X 有没有更耐用?
iOS 11 和 iPhone X:UINavigationBar 的工具栏间距在嵌入到 UITabBarController 时不正确