ChatGPT解决这个技术问题 Extra ChatGPT

Programmatically get height of navigation bar

I know that the presence of the more view controller (navigation bar) pushes down the UIView by its height. I also know that this height = 44px. I have also discovered that this push down maintains the [self.view].frame.origin.y = 0.

So how do I determine the height of this navigation bar, other than just setting it to a constant?

Or, shorter version, how do I determine that my UIView is showing with the navigation bar on top?

The light bulb started to come on. Unfortunately, I have not discovered a uniform way to correct the problem, as described below.

I believe that my whole problem centers on my autoresizingMasks. And the reason I have concluded that is the same symptoms exist, with or without a UIWebView. And that symptom is that everything is peachy for Portrait. For Landscape, the bottom-most UIButton pops down behind the TabBar.

For example, on one UIView, I have, from top to bottom:

UIView – both springs set (default case) and no struts

UIScrollView - If I set the two springs, and clear everything else (like the UIView), then the UIButton intrudes on the object immediately above it. If I clear everything, then UIButton is OK, but the stuff at the very top hides behind the StatusBar Setting only the top strut, the UIButton pops down behind the Tab Bar.

UILabel and UIImage next vertically – top strut set, flexible everywhere else

Just to complete the picture for the few that have a UIWebView:

UIWebView - Struts: top, left, right Springs: both

UIButton – nothing set, i.e., flexible everywhere

Although my light bulb is dim, there appears to be hope.

Please bear with me because I needed more room than that provided for a short reply comment.

Thanks for trying to understand what I am really fishing for ... so here goes.

1) Each UIViewController (a TabBar app) has a UIImage, some text and whatever on top. Another common denominator is a UIButton on the bottom. On some of the UIViewControllers I have a UIWebView above the UIButton.

So, UIImage, text etc. UIWebView (on SOME) UIButton

Surrounding all the above is a UIScrollView.

2) For those that have a UIWebView, its autoresizingMask looks like:

   —
   |
   —

   ^
   |
   |

|—| ←----→ |—| | | V The UIButton's mask has nothing set, i.e., flexible everywhere

Within my -viewDidLoad, I call my -repositionSubViews within which I do the following:

If there is no UIWebView, I do nothing except center the UIButton that I placed with IB.

If I do have a UIWebView, then I determine its *content*Height and set its frame to enclose the entire content.

UIScrollView *scrollViewInsideWebView = [[webView_ subviews] lastObject];
webViewContentHeight = scrollViewInsideWebView.contentSize.height;
[webView_ setFrame:CGRectMake(webViewOriginX, webViewOriginY,
                          sameWholeViewScrollerWidth, webViewContentHeight)]

Once I do that, then I programmatically push the UIButton down so that it ends up placed below the UIWebView.

Everything works, until I rotate it from Portrait to Landscape.

I call my -repositionSubViews within my -didRotateFromInterfaceOrientation.

Why does the content height of my UIWebView not change with rotation?.

From Portrait to Landscape, the content width should expand and the content height should shrink. It does visually as it should, but not according to my NSLog.

Anyway, with or without a UIWebView, the button I've talked about moves below the TabBar when in Landscape mode but it will not scroll up to be seen. I see it behind the TabBar when I scroll "vigorously", but then it "falls back" behind the TabBar.

Bottom line, this last is the reason I've asked about the height of the TabBar and the NavigationBar because the TabBar plants itself at the bottom of the UIView and the NavigationBar pushes the UIView down.

Now, I'm going to add a comment or two here because they wouldn't have made sense earlier.

With no UIWebView, I leave everything as is as seen by IB.

With a UIWebView, I increase the UIWebView's frame.height to its contentHeight and also adjust upward the height of the surrounding UIScrollView that surrounds all the sub-views.

Well there you have it.


A
Arash Etemad

Do something like this ?

    NSLog(@"Navframe Height=%f",
        self.navigationController.navigationBar.frame.size.height);

The swift version is located here

UPDATE

iOS 13

As the statusBarFrame was deprecated in iOS13 you can use this:

extension UIViewController {

    /**
     *  Height of status bar + navigation bar (if navigation bar exist)
     */

    var topbarHeight: CGFloat {
        return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
            (self.navigationController?.navigationBar.frame.height ?? 0.0)
    }
}

Thank you, thank you. BUT, now that you've solved that problem, just how do I determine if the NavigationController, aka, the MoreViewController, is showing for my tab bar app.
I don't quite get what you are trying to say. Are you trying to figure out if MoreViewController is showing? (If so, what do you want to do?) and if not, could you clarify what exactly you mean?
I read your question again, and I still didn't understand what you want to do? If you want to do something when the view appears, you should insert the code into viewDidAppear. If you clarify a bit, it would make it easier to figure what you want to do.
I know this does not address the question he asked, but it seems he was looking to see if the navigationBar was hidden or not. If so, he could have used self.navigationController.navigationBarHidden Hope fully this helps someone :)
In iOS 7+ you may need to also consider the 20px status bar depending on your application
K
Krunal

With iPhone-X, height of top bar (navigation bar + status bar) is changed (increased).

Try this if you want exact height of top bar (both navigation bar + status bar):

UPDATE

iOS 13

As the statusBarFrame was deprecated in iOS13 you can use this:

extension UIViewController {

    /**
     *  Height of status bar + navigation bar (if navigation bar exist)
     */

    var topbarHeight: CGFloat {
        return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
            (self.navigationController?.navigationBar.frame.height ?? 0.0)
    }
}

Objective-C

CGFloat topbarHeight = ([UIApplication sharedApplication].statusBarFrame.size.height +
       (self.navigationController.navigationBar.frame.size.height ?: 0.0));

Swift 4

let topBarHeight = UIApplication.shared.statusBarFrame.size.height +
        (self.navigationController?.navigationBar.frame.height ?? 0.0)

For ease, try this UIViewController extension

extension UIViewController {

    /**
     *  Height of status bar + navigation bar (if navigation bar exist)
     */

    var topbarHeight: CGFloat {
        return UIApplication.shared.statusBarFrame.size.height +
            (self.navigationController?.navigationBar.frame.height ?? 0.0)
    }
}

Swift 3

let topBarHeight = UIApplication.sharedApplication().statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)


The Swift 4 answer returns a 0 height for me.
K
King-Wizard

Swift version:

let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height

Always returns 44. How to know collapsed size (44) or expanded size (large titles)??? Dynamically, of course. (I know what it measures)
L
Lal Krishna

iOS 14

For me, view.window is null on iOS 14.

extension UIViewController {
    var topBarHeight: CGFloat {
        var top = self.navigationController?.navigationBar.frame.height ?? 0.0
        if #available(iOS 13.0, *) {
            top += UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
        } else {
            top += UIApplication.shared.statusBarFrame.height
        }
        return top
    }
}

Y
Yonathan Goriachnick

Swift 5

If you want to get the navigation bar height, use the maxY property that considers the safeArea size as well, like this:

 let height = navigationController?.navigationBar.frame.maxY  

Should be the answer. You save my day.
C
Community

Support iOS 13 and Below:

extension UIViewController {

    var topbarHeight: CGFloat {
        if #available(iOS 13.0, *) {
            return (view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0) +
                (self.navigationController?.navigationBar.frame.height ?? 0.0)
        } else {
            let topBarHeight = UIApplication.shared.statusBarFrame.size.height +
            (self.navigationController?.navigationBar.frame.height ?? 0.0)
            return topBarHeight
        }
    }
}

for me, view.window is nil. So i've to take frame from UIApplication.shared.windows's keywnidow. (iOS 14)
J
Juri Noga

Did you try this?

let barHeight = self.navigationController?.navigationBar.frame.height ?? 0

i
ironik sütlaç
UIImage*image = [UIImage imageNamed:@"logo"];

float targetHeight = self.navigationController.navigationBar.frame.size.height;
float logoRatio = image.size.width / image.size.height;
float targetWidth = targetHeight * logoRatio;

UIImageView*logoView = [[UIImageView alloc] initWithImage:image];
// X or Y position can not be manipulated because autolayout handles positions.
//[logoView setFrame:CGRectMake((self.navigationController.navigationBar.frame.size.width - targetWidth) / 2 , (self.navigationController.navigationBar.frame.size.height - targetHeight) / 2 , targetWidth, targetHeight)];
[logoView setFrame:CGRectMake(0, 0, targetWidth, targetHeight)];
self.navigationItem.titleView = logoView;

// How much you pull out the strings and struts, with autolayout, your image will fill the width on navigation bar. So setting only height and content mode is enough/
[logoView setContentMode:UIViewContentModeScaleAspectFit];

/* Autolayout constraints also can not be manipulated since navigation bar has  immutable constraints
self.navigationItem.titleView.translatesAutoresizingMaskIntoConstraints = false;

NSDictionary*metricsArray = @{@"width":[NSNumber numberWithFloat:targetWidth],@"height":[NSNumber numberWithFloat:targetHeight],@"margin":[NSNumber numberWithFloat:20]};
NSDictionary*viewsArray = @{@"titleView":self.navigationItem.titleView};

[self.navigationItem.titleView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>margin=)-H:[titleView(width)]-(>margin=)-|" options:NSLayoutFormatAlignAllCenterX metrics:metricsArray views:viewsArray]];
[self.navigationItem.titleView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[titleView(height)]" options:0 metrics:metricsArray views:viewsArray]];

NSLog(@"%f", self.navigationItem.titleView.width );
*/

So all we actually need is

UIImage*image = [UIImage imageNamed:@"logo"];
UIImageView*logoView = [[UIImageView alloc] initWithImage:image];
float targetHeight = self.navigationController.navigationBar.frame.size.height;
[logoView setFrame:CGRectMake(0, 0, 0, targetHeight)];
[logoView setContentMode:UIViewContentModeScaleAspectFit];

self.navigationItem.titleView = logoView;

C
CodenameDuchess

Handy Swift 4 extension, in case it's helpful to someone else. Works even if the current view controller does not display a navigation bar.

import UIKit

extension UINavigationController {
  static public func navBarHeight() -> CGFloat {
    let nVc = UINavigationController(rootViewController: UIViewController(nibName: nil, bundle: nil))
    let navBarHeight = nVc.navigationBar.frame.size.height
    return navBarHeight
  }
}

Usage:

UINavigationController.navBarHeight()

Nice solution as a static member in cases you don't have a VC
佚名

The light bulb started to come on. Unfortunately, I have not discovered a uniform way to correct the problem, as described below.

I believe that my whole problem centers on my autoresizingMasks. And the reason I have concluded that is the same symptoms exist, with or without a UIWebView. And that symptom is that everything is peachy for Portrait. For Landscape, the bottom-most UIButton pops down behind the TabBar.

For example, on one UIView, I have, from top to bottom:

UIView – both springs set (default case) and no struts

UIScrollView - If I set the two springs, and clear everything else (like the UIView), then the UIButton intrudes on the object immediately above it. If I clear everything, then UIButton is OK, but the stuff at the very top hides behind the StatusBar Setting only the top strut, the UIButton pops down behind the Tab Bar.

UILabel and UIImage next vertically – top strut set, flexible everywhere else

Just to complete the picture for the few that have a UIWebView:

UIWebView - Struts: top, left, right Springs: both

UIButton – nothing set, i.e., flexible everywhere

Although my light bulb is dim, there appears to be hope.


Set top strut and set the two springs of the UIWebView - problem solved and thanks bunches. As a matter of fact, my only remaining problem is to design those cursed @2X, etc. graphics. I use GraphicConverter in a very simple way and it does well for Web Page design, very well. But all these 30px, 57px stuff - I'm going into unchartered territory for me, at least.
S
Stephen Liu

My application has a couple views that required a customized navigation bar in the UI for look & feel, however without navigation controller. And the application is required to support iOS version prior to iOS 11, so the handy safe area layout guide could not be used, and I have to adjust the position and height of navigation bar programmatically.

I attached the Navigation Bar to its superview directly, skipping the safe area layout guide as mentioned above. And the status bar height could be retrieved from UIApplication easily, but the default navigation bar height is really a pain-ass...

It struck me for almost half a night, with a number of searching and testing, until I finally got the hint from another post (not working to me though), that you could actually get the height from UIView.sizeThatFits(), like this:

- (void)viewWillLayoutSubviews {
    self.topBarHeightConstraint.constant = [UIApplication sharedApplication].statusBarFrame.size.height;
    self.navBarHeightConstraint.constant = [self.navigationBar sizeThatFits:CGSizeZero].height;

    [super viewWillLayoutSubviews];    
}

Finally, a perfect navigation bar looking exactly the same as the built-in one!


m
mojtaba al moussawi

If you want to get the navigationBar height only, it's simple:

extension UIViewController{
   var navigationBarHeight: CGFloat {
       return self.navigationController?.navigationBar.frame.height ?? 0.0
   }
}

However, if you need the height of top notch of iPhone you don't need to get the navigationBar height and add to it the statusBar height, you can simply call safeAreaInsets that's why exist.

self.view.safeAreaInsets.top

S
Sum

Here is the beginning of my response to your update:

Why does the content height of my UIWebView not change with rotation?.

Could it be that because your auto resize doesn't have the autoresizingMask for all directions?

Another suggestion before I come back for this, could you use a toolbar for your needs. It's a little simpler, will always be on the bottom, auto-rotates/positions. You can hide/show it at will etc. Kind of like this: http://cdn.artoftheiphone.com/wp-content/uploads/2009/01/yellow-pages-iphone-app-2.jpg

You may have looked at that option, but just throwing it out there.

Another idea, could you possibly detect what orientation you are rotating from, and just place the button programmatically to adjust for the tab bar. (This is possible with code)


After about 2 weeks on tackling this, I decided that the "problem" was made more difficult by placing a UIButton BELOW the UIWebView sub-view. So, I put the button ABOVE it. Quite frankly, it just doesn't look "right" that way ... but it now works SORT OF. BELOW or ABOVE the UIWebView has set the top strut and just the horizontal spring. BTW, its the contentHeight of the UIWebView that does not change with rotation.
u
user3332228

I have used:

let originY: CGFloat = self.navigationController!.navigationBar.frame.maxY

Working great if you want to get the navigation bar height AND its Y origin.


This is the best way to do it in my opinion because the maxY value is the height of the navbar and status bar.
m
multitudes

Swift : programmatically adding a web view right under the navigation bar

From iOS11 the key to position the view below the navigation bar is to use safeAreaLayoutGuide

From the Apple docs (link):

The layout guide representing the portion of your view that is unobscured by bars and other content.

So in code I will put the top constraint using view.safeAreaLayoutGuide.topAnchor

Again the whole thing will be for example:

import WebKit

class SettingsViewController: UIViewController {
let webView = WKWebView()
let url = URL(string: "https://www.apple.com")

override func viewDidLoad() {
        super.viewDidLoad()
        configureWebView()
}

override func viewWillAppear(_ animated: Bool) {
        follow(url: url)
}

func follow(url: URL?) {
        if let url = url {
            let request = URLRequest(url: url)
            webView.load(request)
    }
}

func configureWebView() {
        view.addSubview(webView)
        webView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
}
}