I've found a (hacky) solution that works in iOS 14 (untested in other versions). It makes an assumption about the private view structure of UINavigationBar
, so there's no guarantee that it will work in future iOS versions, but it's unlikely to crash - the worst that should happen is that the bar will fail to hide, or only partially hide.
Assuming that you are placing the code inside a UIViewController
subclass that it acting as the delegate for a UITableView
, UICollectionView
or UIScrollView
, the following should work:
override func viewDidLoad() {
super.viewDidLoad()
// this hides the bar initially
self.navigationController?.navigationBar.subviews.first?.alpha = 0
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let navigationController = self.navigationController else { return }
let navBarHeight = navigationController.navigationBar.frame.height
let threshold: CGFloat = 20 // distance from bar where fade-in begins
let alpha = (scrollView.contentOffset.y + navBarHeight + threshold) / threshold
navigationController.navigationBar.subviews.first?.alpha = alpha
}
The magic threshold
value is a little hard to explain, but it's basically the distance from the bar at which the fade in will start. A value of 20 means the bar starts to fade in when the scrollView content is 20 points away. A value of 0 would mean the bar snaps straight from fully transparent to fully opaque the moment the scrollView content touches it.