Reputation: 30336
I have enabled large titles for the navigation bar with:
navigationController?.navigationBar.prefersLargeTitles = true
This makes the navigation bar start with an expanded height, and shrink as the user scrolls down.
Now, I want to add a subview inside the navigation bar that resizes, based on how tall the navigation bar is. To do this, I will need to get both the maximum and minimum height of the navigation bar, so I can calculate the fraction of how much it's expanded.
I can get the current height of the navigation bar like this:
guard let height = navigationController?.navigationBar.frame.height else { return }
print("Navigation height: \(height)")
I'm calling this inside scrollViewDidScroll
, and as I'm scrolling, it seems that the expanded height is around 96 and the shrunk height is around 44. However, I don't want to hardcode values.
iPhone 12
iPhone 8
I am also only able to get these values when the user physically scrolls up and down, which won't work in production. And even if I forced the user to scroll, it's still too late, because I need to know both heights in advance so I can insert my resizing subview.
I want to get these values, but without hardcoding or scrolling |
---|
Is there any way I can get the height of both the shrunk and expanded navigation bar?
Upvotes: 4
Views: 1568
Reputation: 512
Works with iOS 16 and above.
private var smallNavigationBarHeight: Double?
private var largeNavigationBarHeight: Double?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.largeTitleDisplayMode = .never
smallNavigationBarHeight = navigationController?.navigationBar.frame.height ?? 44
navigationItem.largeTitleDisplayMode = .always
largeNavigationBarHeight = navigationController?.navigationBar.frame.height ?? 96
}
Upvotes: 0
Reputation: 30336
Came across my own question a year later. The other answer didn't work, so I used the view hierarchy.
It seems that the shrunk appearance is embedded in a class called _UINavigationBarContentView
. Since this is a private class, I can't directly access it. But, its y
origin is 0
and it has a UILabel
inside it. That's all I need to know!
extension UINavigationBar {
func getCompactHeight() -> CGFloat {
/// Loop through the navigation bar's subviews.
for subview in subviews {
/// Check if the subview is pinned to the top (compact bar) and contains a title label
if subview.frame.origin.y == 0 && subview.subviews.contains(where: { $0 is UILabel }) {
return subview.bounds.height
}
}
return 0
}
}
Usage:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Navigation"
if
let navigationBar = navigationController?.navigationBar,
let window = UIApplication.shared.keyWindow
{
navigationBar.prefersLargeTitles = true /// Enable large titles.
let compactHeight = navigationBar.getCompactHeight() // 44 on iPhone 11
let statusBarHeight = window.safeAreaInsets.top // 44 on iPhone 11
let navigationBarHeight = compactHeight + statusBarHeight
print(navigationBarHeight) // Result: 88.0
}
}
The drawback of this answer is if Apple changes UINavigationBar
's internals, it might not work. Good enough for me though.
Upvotes: 2
Reputation: 1249
Using following extension u can get extra height
extension UINavigationBar
{
var largeTitleHeight: CGFloat {
let maxSize = self.subviews
.filter { $0.frame.origin.y > 0 }
.max { $0.frame.origin.y < $1.frame.origin.y }
.map { $0.frame.size }
return maxSize?.height ?? 0
}
}
And I said earlier u can get extended height by following
guard let height = navigationController?.navigationBar.frame.maxY else { return }
print("Navigation height: \(height)")
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let extendedHeight = height - topPadding
You can get shrunk height by subtracting difference from extended height
guard let difference = navigationController?.navigationBar.lagreTitleHeight else {return}
let shrunkHeight = extendedHeight - difference
Upvotes: 0