The behavior of a UINavigationBar when navigating the stack can seem unpredictable and often buggy. But, in fact, it is! This article aims to refresh your knowledge of the principles of work and show the possibilities for customizing behavior.
Some general theory
If you are knowledgeable, feel free to scroll directly to the animation.
UINavigationBar is a view. Typically, its position is controlled by the UINavigationController, but like other views, you can use it yourself.
UINavigationItem is a class that describes the state (similar to the viewModel) for the UINavigationBar configuration. Just a class with properties that will be passed to the UINavigationBar.
The UINavigationBar contains an array [UINavigationItem]. Using the pushItem, popItem, and setItems methods, you can animate the transition from one UINavigationBar state to another.
Each UIViewController contains a UINavigationItem. The UINavigationController itself manages the addition of this property to the stack of UINavigationBar items.
More details can be found here:
https://developer.apple.com/documentation/uikit/uinavigationbar
https://developer.apple.com/documentation/uikit/uinavigationitem
UINavigationBar . :
prompt ( )
largeTitleDisplayMode
UINavigationBar – view, – , , .
: – navigationBar.backgroundColor, – navigationBar.barTintColor.
– backgroundColor. , backgroundColor – view -.
prompt- .
, transition UINavigationBar . UIViewControllerAnimatedTransitioning UINavigationBar.
: , .
safeArea . additionalSafeAreaInsets UIViewController- :
contentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
UINavigationController UINavigationControllerDelegate. , .
class NavigationController: UINavigationController, UINavigationControllerDelegate { }
UINavigationController-.
navigationController.delegate = navigationController
. UIViewController navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) transitionCoordinator safeArea.
guard let fromViewController = viewController.transitionCoordinator?.viewController(forKey: .from)
else { return }
// . , , pop
let isPopped = !navigationController.viewControllers.contains(fromViewController)
// ,
viewController.transitionCoordinator?.animate { context in
guard let from = context.viewController(forKey: .from),
let to = context.viewController(forKey: .to)
else { return }
//
// , safeArea
//
UIView.setAnimationsEnabled(false)
let diff = to.view.safeAreaInsets.top - from.view.safeAreaInsets.top
//
to.additionalSafeAreaInsets.top = -diff
to.view.layoutIfNeeded()
UIView.setAnimationsEnabled(true)
// safeArea
to.additionalSafeAreaInsets.top = 0
to.view.layoutIfNeeded()
guard isPopped else { return }
// pop-
// additionalSafeAreaInsets
from.view.frame.origin.y = diff
from.view.frame.size.height += max(0, -diff)
} completion: { context in
guard let from = context.viewController(forKey: .from),
let to = context.viewController(forKey: .to)
else { return }
from.additionalSafeAreaInsets.top = 0
to.additionalSafeAreaInsets.top = 0
}
, :
– . , UINavigationBar. , , .
, :)
: Rtishchev Evgenii https://twitter.com/katleta3000/status/1259400743771156480
navigation bar: https://www.programmersought.com/article/1594185256/