以编程方式获取导航栏的高度

Posted

技术标签:

【中文标题】以编程方式获取导航栏的高度【英文标题】:Programmatically get height of navigation bar 【发布时间】:2011-11-10 20:11:49 【问题描述】:

我知道更多视图控制器(导航栏)的存在将 UIView 的高度向下推。我也知道这个高度 = 44px。我还发现这个下推保持[self.view].frame.origin.y = 0

那么我如何确定这个导航栏的高度,而不只是将它设置为一个常数呢?

或者,更短的版本,我如何确定我的 UIView 显示时导航栏在顶部?


灯泡开始亮了。不幸的是,我还没有找到一种统一的方法来纠正这个问题,如下所述。

我相信我的整个问题都集中在我的 autoresizingMasks 上。我得出结论的原因是存在相同的症状,无论有没有 UIWebView。那个症状是肖像的一切都是桃色的。对于横向,最底部的 UIButton 在 TabBar 后面弹出。

例如,在一个 UIView 上,我有,从上到下:

UIView - 两个弹簧设置(默认情况)和没有支柱

UIScrollView - 如果我设置了两个弹簧,并清除了其他所有内容(如 UIView),那么 UIButton 会侵入其正上方的对象。如果我清除所有内容,那么 UIButton 就可以了,但是最顶部的东西隐藏在 StatusBar 后面 仅设置顶部支柱,UIButton 在 Tab Bar 后面弹出。

UILabel 和 UIImage 下一个垂直 - 顶部支柱集,其他任何地方都灵活

只是为了完成少数拥有 UIWebView 的图片:

UIWebView - Struts:上、左、右 Springs:两者

UIButton – 没有设置,即随处灵活

虽然我的灯泡很暗,但似乎还有希望。


请多多包涵,因为我需要比提供简短回复评论更多的空间。

感谢您尝试了解我真正想要的是什么......所以就这样吧。

1) 每个 UIViewController(一个 TabBar 应用程序)都有一个 UIImage、一些文本以及顶部的任何内容。另一个共同点是底部的 UIButton。在一些 UIViewControllers 上,我在 UIButton 上方有一个 UIWebView。

所以,UIImage、文本等 UIWebView (on SOME) UIButton

围绕着以上所有的是一个 UIScrollView。

2) 对于那些有 UIWebView 的,它的 autoresizingMask 看起来像:

   —
   |
   —

   ^
   |
   |

|—| ←----→ |—| | | 五 UIButton 的掩码没有任何设置,即在任何地方都灵活

在我的 -viewDidLoad 中,我调用我的 -repositionSubViews,在其中我执行以下操作:

如果没有 UIWebView,除了将我放置在 IB 中的 UIButton 居中之外,我什么都不做。

如果我确实有一个 UIWebView,那么我确定它的 *content*Height 并设置它的框架以包含整个内容。

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

完成此操作后,我会以编程方式将 UIButton 向下推,使其最终放置在 UIWebView 下方。

一切正常,直到我将它从纵向旋转到横向。

我在我的 -didRotateFromInterfaceOrientation 中调用我的 -repositionSubViews。

为什么我的 UIWebView 的内容高度不随旋转而变化?

从纵向到横向,内容宽度应该扩大,内容高度应该缩小。它在视觉上是应有的,但不是根据我的 NSLog。

无论如何,无论有没有 UIWebView,我谈到的按钮在横向模式下都会移动到 TabBar 下方,但不会向上滚动以显示。当我“大力”滚动时,我在 TabBar 后面看到它,但随后它“退回”到 TabBar 后面。

底线,这最后是我询问 TabBar 和 NavigationBar 高度的原因,因为 TabBar 将自身放置在 UIView 的底部,而 NavigationBar 将 UIView 向下推。

现在,我将在这里添加一两条评论,因为它们之前没有意义。

在没有 UIWebView 的情况下,我保留 IB 所看到的一切。

使用 UIWebView,我将 UIWebView 的 frame.height 增加到它的 contentHeight 并向上调整围绕所有子视图的 UIScrollView 的高度。

你有它。

【问题讨论】:

【参考方案1】:

Swift : 以编程方式在导航栏下方添加一个 Web 视图

ios11开始,定位导航栏下方视图的关键是使用safeAreaLayoutGuide

来自 Apple 文档 (link):

表示视图中未被条形和其他内容遮挡的部分的布局指南。

所以在代码中我将使用view.safeAreaLayoutGuide.topAnchor 放置顶部约束

再一次举个例子:

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)
        ])


【讨论】:

【参考方案2】:

斯威夫特 5

如果要获取导航栏高度,请使用maxY 属性,该属性也考虑了安全区域的大小,如下所示:

 let height = navigationController?.navigationBar.frame.maxY  

【讨论】:

应该是答案。你拯救了我的一天。【参考方案3】:

iOS 14

对我来说,view.window 在 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
    

【讨论】:

【参考方案4】:

方便的 Swift 4 扩展,以防它对其他人有帮助。即使当前视图控制器不显示导航栏也可以工作。

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
  

用法:

UINavigationController.navBarHeight()

【讨论】:

在没有 VC 的情况下作为静态成员的不错的解决方案【参考方案5】:

支持 iOS 13 及以下:

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
        
    

【讨论】:

对我来说,view.window 是零。所以我必须从UIApplication.shared.windows 的keywnidow 中取帧。 (iOS 14)【参考方案6】:

如果你只想得到navigationBar的高度,很简单:

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

但是,如果您需要 iPhone 的***高度,则无需获取navigationBar 高度并将其添加到statusBar 高度,您只需调用safeAreaInsets 这就是存在的原因。

self.view.safeAreaInsets.top

【讨论】:

【参考方案7】:

在 iPhone-X 中,顶部栏(导航栏 + 状态栏)的高度发生了变化(增加)。

如果您想要顶部栏的确切高度(导航栏+状态栏),请尝试此操作:

更新

iOS 13

由于statusBarFrameiOS13 中已被弃用,您可以使用它:

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));

斯威夫特 4

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

为了方便,试试这个 UIViewController 扩展

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)
    

斯威夫特 3

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

【讨论】:

Swift 4 答案为我返回 0 高度。【参考方案8】:

做这样的事吗?

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

swift版是located here


更新

iOS 13

由于statusBarFrameiOS13 中已弃用,您可以使用它:

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)
    

【讨论】:

谢谢,谢谢。但是,既然您已经解决了这个问题,那么我该如何确定 NavigationController(又名 MoreViewController)是否正在为我的标签栏应用程序显示。 我不太明白你想说什么。您是否想弄清楚 MoreViewController 是否正在显示? (如果是,你想做什么?)如果不是,你能澄清一下你的意思吗? 我又看了你的问题,还是没明白你想做什么?如果你想在视图出现时做一些事情,你应该将代码插入到 viewDidAppear 中。如果你澄清一点,它会更容易弄清楚你想做什么。 我知道这并没有解决他提出的问题,但似乎他正在查看 navigationBar 是否隐藏。如果是这样,他本可以使用 self.navigationController.navigationBarHidden 希望这对某人有帮助:) 在 iOS 7+ 中,您可能还需要根据您的应用程序考虑 20px 状态栏【参考方案9】:

我用过:

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

如果您想获取导航栏高度及其 Y 原点,那就太好了。

【讨论】:

我认为这是最好的方法,因为 maxY 值是导航栏和状态栏的高度。【参考方案10】:

我的应用程序有几个视图,需要在 UI 中自定义导航栏以获得外观,但没有导航控制器。并且应用程序需要支持iOS 11之前的iOS版本,所以无法使用方便的安全区域布局指南,我必须通过编程调整导航栏的位置和高度。

我直接将导航栏附加到它的超级视图,跳过了上面提到的安全区域布局指南。并且状态栏高度可以很容易地从 UIApplication 中获取,但是默认的导航栏高度真的很麻烦......

它让我震惊了将近半个晚上,经过多次搜索和测试,直到我终于从另一篇文章中得到了提示(虽然对我不起作用),你实际上可以从 UIView.sizeThatFits() ,像这样:

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

    [super viewWillLayoutSubviews];    

最后,一个完美的导航栏看起来和内置的一模一样!

【讨论】:

【参考方案11】:

你试过了吗?

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

【讨论】:

【参考方案12】:
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 );
*/

所以我们真正需要的是

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;

【讨论】:

【参考方案13】:

Swift 版本:

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

【讨论】:

总是返回44。如何知道折叠尺寸(44)或展开尺寸(大标题)???当然是动态的。 (我知道它测量的是什么)【参考方案14】:

这是我对您的更新的回应的开始:

为什么我的 UIWebView 的内容高度不随旋转而变化?

可能是因为您的自动调整大小没有针对所有方向的 autoresizingMask 吗?

在我回来之前的另一个建议是,您能否根据需要使用工具栏。它有点简单,总是在底部,自动旋转/定位。您可以随意隐藏/显示它等。有点像这样:http://cdn.artoftheiphone.com/wp-content/uploads/2009/01/yellow-pages-iphone-app-2.jpg

您可能已经看过该选项,但只是将其扔掉。

另一个想法,您是否可以检测到您旋转的方向,然后以编程方式放置按钮以调整标签栏。 (这可以通过代码实现)

【讨论】:

在解决这个问题大约 2 周后,我决定通过在 UIWebView 子视图下方放置一个 UIButton 来使“问题”变得更加困难。所以,我把按钮放在它上面。坦率地说,它看起来并不“正确”……但它现在可以正常工作了。 UIWebView 的下方或上方设置了顶部支柱和水平弹簧。顺便说一句,它的 UIWebView 的 contentHeight 不会随旋转而改变。【参考方案15】:

灯泡开始亮了。不幸的是,我还没有找到一种统一的方法来纠正这个问题,如下所述。

我相信我的整个问题都集中在我的 autoresizingMasks 上。我得出结论的原因是存在相同的症状,无论有没有 UIWebView。那个症状是肖像的一切都是桃色的。对于横向,最底部的 UIButton 在 TabBar 后面弹出。

例如,在一个 UIView 上,我有,从上到下:

UIView – 两个弹簧设置(默认情况)和没有支柱

UIScrollView - 如果我设置了两个弹簧,并清除了其他所有内容(如 UIView),那么 UIButton 会侵入它正上方的对象。 如果我清除所有内容,那么 UIButton 就可以了,但最上面的东西隐藏在 StatusBar 后面 仅设置顶部支柱,UIButton 会在 Tab Bar 后面弹出。

UILabel 和 UIImage 下一个垂直 - 顶部支柱设置,其他任何地方都灵活

只是为了完成少数拥有 UIWebView 的图片:

UIWebView - 支柱:上、左、右 弹簧:两者

UIButton – 没有设置,即随处灵活

虽然我的灯泡很暗,但似乎还有希望。

【讨论】:

设置顶部支柱并设置 UIWebView 的两个弹簧 - 问题解决了,谢谢大家。事实上,我剩下的唯一问题就是设计那些被诅咒的@2X等图形。我以一种非常简单的方式使用 GraphicConverter,它非常适合网页设计,非常好。但是所有这些 30 像素、57 像素的东西 - 至少我要进入未知领域。

以上是关于以编程方式获取导航栏的高度的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式快速更改导航栏的高度

如何以编程方式设置导航栏的标题?

如何获取Android手机底部导航栏的高度

自定义以编程方式创建的导航栏的背景图像

以编程方式更改标签栏和导航栏的颜色

以编程方式显示导航栏的 UISearchController