UITabBarController 的问题是它的 TabBar 是在没有使用 NSLayoutConstraints 的情况下添加的(或者更准确地说,它将自动调整大小的掩码转换为约束)。因此,您可以使用两种方法:
1) 以您现在的方式使用 UITabBarController,但它需要一些技巧来隐藏它 - 基本上在 UINavigationController 中使用 UITabBarController 以便在其上推送一个视图(但转换将是可见的,即使您会在没有动画的情况下推送它(键盘会开始隐藏),或者您可以手动隐藏 TabBar 并调整 TabBar 内容视图的框架,如https://stackoverflow.com/a/6346096/7183675所示)。
在最后一种情况下,您还必须在更改内容视图之前记住它的框架(或在再次取消隐藏 TabBar 之前计算它)。此外,由于它不在官方 API 中,您必须考虑到 UITabBarController 中的子视图顺序可能会发生变化,并且效果可能看起来很奇怪(或者只是让应用程序崩溃)
2) 将“普通”UIViewController 与 UITabBar 一起使用,并使用约束手动添加其项目。它也可以是自定义 UIView 子类和从 XIB 创建的几个按钮。在这里,您直接创建约束,因此您可以更好地控制。
但是这个也不会没有一些技巧,因为添加到单个 UIViewController 的 UITabBar 会在每次转换时与这个 UIViewController 一起使用(假设你在每个 UIViewController 中都有 UINavigationController 它会很常见)。
因此,在这种情况下,主要问题是制作单个底栏并将其传输到 viewDidAppear 上的 UIWindow,其中创建了您唯一的底栏 - 从情节提要或 xib 文件中推荐。对于下一个视图,您只会传递对它的引用或将此指针保留在一个类中。您还应该记住在标签栏下创建覆盖安全区域的视图。
看起来像这样:
private var firstRun = false
override func viewDidLoad() {
super.viewDidLoad()
firstRun = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard firstRun else {
bottomBar.superview?.bringSubviewToFront(bottomBar)
bottomSafeAreaView.superview?.bringSubviewToFront(bottomSafeAreaView)
return
}
guard let window = UIApplication.shared.windows.first, let bottomB = bottomBar, let bottomSafeArea = bottomSafeAreaView else { return }
if bottomB.superview != window {
bottomB.deactivateConstrainsToSuperview()
bottomSafeArea.deactivateConstrainsToSuperview()
window.addSubview(bottomSafeArea)
window.addSubview(bottomB)
let bottomLeft = NSLayoutConstraint(item: bottomSafeArea, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0)
let bottomRight = NSLayoutConstraint(item: bottomSafeArea, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0)
let bottomBottom = NSLayoutConstraint(item: bottomSafeArea, attribute: .bottom, relatedBy: .equal, toItem: window, attribute: .bottom, multiplier: 1, constant: 0)
let leftConstraint = NSLayoutConstraint(item: bottomB, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0)
let rightConstraint = NSLayoutConstraint(item: bottomB, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: bottomB, attribute: .bottom, relatedBy: .equal, toItem: bottomSafeArea, attribute: .top, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([bottomLeft, bottomRight, bottomBottom, leftConstraint, rightConstraint, bottomConstraint])
}
window.layoutIfNeeded()
DispatchQueue.main.async(execute: {
bottomB.superview?.bringSubviewToFront(bottomB)
bottomSafeArea.superview?.bringSubviewToFront(bottomSafeArea)
})
firstRun = false
}
加上一个在扩展中创建的实用方法:
extension UIView {
func deactivateConstrainsToSuperview() {
guard let superview = self.superview else {return}
NSLayoutConstraint.deactivate(self.constraints.filter({
return ($0.firstItem === superview || $0.secondItem === superview)
}))
}
}
所以要写一点代码,但只能写一次。之后,您将拥有在必要时易于显示或隐藏的 TabBar,以这种方式在“内容视图”和安全区域之间使用约束
private func hideBottomBar() {
UIView.animate(withDuration: Constants.appAnimation.duration, animations: { [weak self] in
guard let self = self else { return }
self.bottomBar.isHidden = true
self.bottomBarHeightConstraint.constant = 0
self.bottomBar.superview?.layoutIfNeeded()
})
}
和
private func showBottomBar() {
UIView.animate(withDuration: Constants.appAnimation.duration, animations: { [weak self] in
guard let self = self else { return }
self.bottomBar.isHidden = false
self.bottomBarHeightConstraint.constant = Constants.appConstraintsConstants.bottomBarHeight
self.bottomBar.superview?.layoutIfNeeded()
})
}
关于覆盖安全区域的视图高度(tabBar底部和BottomLayoutGuide顶部之间)
if #available(iOS 11.0, *) {
self.bottomSafeAreaViewHeightConstraint.constant = self.view.safeAreaInsets.bottom
} else {
self.bottomSafeAreaViewHeightConstraint.constant = 0
}
希望对你有所帮助,祝你好运!