Get the default shrunk and expanded height of large title navigation bar

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

Expanded (96.33) Shrunk (44)
enter image description here enter image description here

iPhone 8

Expanded (96.5) Shrunk (44)
enter image description here enter image description here

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?


Came across my own question a year later. The other answer didn't work, so I used the view hierarchy.

View Hierarchy with '_UINavigationBarContentView" selected

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.