Reputation: 3660
I have an issue with the hidesBottomBarWhenPushed
.
It works correctly when pushed (hides tab bar) and when popped (shows tab bar). But I have this little gem:
hidesBottomBarWhenPushed
set to true in the init. The tab bar is hidden correctlySo sadly since there are no official functions to call on the UITabBarController to hide or show the UITabBar I don't see a good way to fix this. There is also no function to reevaluate the current state.
If the presentation is slightly delayed all works fine (but well there are some stakeholders who don't want that...)
So this is the complete code that reproduces the bug (assumes a new project with a storyboard where you have a UITabBarController as the entry point, in it a single UINavigationController with the ViewController
as its child)
Please forgive the 'uglyness', it is just simple demo code
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.tabBar.isTranslucent = false
view.backgroundColor = .red
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.navigationController?.pushViewController(VC2(), animated: true)
}
}
}
class VC2: UIViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
hidesBottomBarWhenPushed = true
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction(handler: { _ in
let nvc = self.navigationController
let vc = UINavigationController(rootViewController: VC3())
vc.modalPresentationStyle = .fullScreen
// With a delay it all works fine
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
nvc?.present(vc, animated: true)
// }
self.navigationController?.popViewController(animated: true)
}), menu: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
}
class VC3: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction(handler: { _ in
self.dismiss(animated: true)
}))
}
}
Upvotes: 0
Views: 117
Reputation: 3660
And I found another solution. This only works if the order is something like
vc.present(modalVC, animated: true)
...
self.navigationController?.popViewController(animated: false)
and not the other way around (so the present must be done before the pop)
var animatedPop = true
if let presentedViewController, presentedViewController.modalPresentationStyle == .fullScreen {
animatedPop = false
}
navigationController?.popViewController(animated: animatedPop)
Apparently for some magical reason the system does evaluate the hidesBottomBarWhenPushed
correctly if not animated
Upvotes: 0
Reputation: 3660
Seems that a 'hacky' solution is to make a modification to the 'owning' UINavigationController
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Determine if we are in the invalid state
guard
let tabBarController,
tabBarController.tabBar.isHidden,
let last = children.last,
!last.hidesBottomBarWhenPushed
else {
return
}
// Force the navigation controller to reevaluate the current `hidesBottomBarWhenPushed`
pushViewController(UIViewController(), animated: false)
popViewController(animated: false)
// The safe space is shown but the tab bar is still set to hidden
tabBarController.tabBar.isHidden = false
}
For me this works without any visual artifacts, but it is also a bit hacky which is always a questionable solution.
Upvotes: 0