Lets's say, I have 3 view controllers: A
, B
, C
, all embedded into a navigation controller. A
and B
have a navigation bar, C
doesn't.
I have a custom interactive transition between B
and C
. Since I need my navigation bar to disappear on C
, I implemented this function of UINavigationControllerDelegate
:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if viewController is C {
navigationController.setNavigationBarHidden(true, animated: animated)
}
else {
navigationController.setNavigationBarHidden(false, animated: animated)
}
}
Everything works perfectly in an common scenario, when I only make push-pop transitions.
But when I cancel transition B
->C
by calling cancel()
on my UIPercentDrivenInteractiveTransition
, the navigation bar doesn't show up on B
. Here I have to call setNavigationBarHidden(false, ...)
, but I haven't managed to find the correct place to do it.
If I call it in B
's viewWillAppear
, navigation bar appears, but looks really strange - it contains elements which the C
would have if it had navigation bar. If I pop back to the A
, it will blink for a moment with expected contents, but immediately after transition the A
navigation bar gets replaced by the B
navigation bar!
So, it seems like the navigation bars stack is somehow broken after B
->C
transition cancellation, it appears to be shifted relatively to viewcontrollers like that:
has
-----------------------------------------------
| ViewController | Navigation bar of |
-----------------------------------------------
| A | B |
-----------------------------------------------
| B | C |
-----------------------------------------------
So, my question is what's the correct place to call navigationController.setNavigationBarHidden(false, animated: true)
in this case?
Well, I've managed to find an ugly hack to fix it by myself. Maybe someone in this world would find it helpful.
In my custom UIPercentDrivenInteractiveTransition
I override cancel
function like that:
class CustomTransitionManager: UIPercentDrivenInteractiveTransition {
/// Indicates transition direction. Must be set before each transition.
var forward: Bool = true
/// Current navigation controller used for transition. Must be set before transition starts
var nc: UINavigationController?
/**
* Hack #1 with UINavigationController here
*/
override func cancel() {
super.cancel()
if forward {
self.nc?.setNavigationBarHidden(false, animated: false)
}
self.nc?.setNavigationBarHidden(forward, animated: false)
}
}
In each of the view controllers (A, B, C) I make the following hack:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide and immediately show navigation bar: this will restore it's correct state
self.navigationController?.setNavigationBarHidden(true, animated: false)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
The best solution probably would be a modal fullscreen presentation of C
, but in my case I am working with a project already having corrupted navigation hierarchy and I had no time to fix it properly. Basically, that's the reason why I faced this issue.