Obter programaticamente a altura da barra de navegação


131

Eu sei que a presença do controlador de mais view (barra de navegação) empurra o UIView pela sua altura. Eu também sei que essa altura = 44px. Eu também descobri que esse push down mantém o [self.view].frame.origin.y = 0.

Então, como eu determino a altura dessa barra de navegação, além de configurá-la para uma constante?

Ou, versão mais curta, como determino que meu UIView está sendo exibido com a barra de navegação na parte superior?


A lâmpada começou a acender. Infelizmente, não descobri uma maneira uniforme de corrigir o problema, conforme descrito abaixo.

Acredito que todo o meu problema esteja centrado nas minhas autoresizingMasks. E a razão pela qual concluí que existem os mesmos sintomas, com ou sem um UIWebView. E esse sintoma é que tudo é pêssego para o Portrait. Para Paisagem, o UIButton mais abaixo aparece atrás da TabBar.

Por exemplo, em um UIView, eu tenho, de cima para baixo:

UIView - ambas as molas definidas (caso padrão) e sem suportes

UIScrollView - Se eu definir as duas molas e limpar tudo o resto (como o UIView), o UIButton entrará no objeto imediatamente acima dele. Se eu limpar tudo, o UIButton está OK, mas as coisas na parte superior se escondem atrás da Configuração da barra de status, apenas a parte superior, o UIButton aparece atrás da barra de guias.

UILabel e UIImage a seguir na vertical - conjunto superior de suporte, flexível em qualquer outro lugar

Apenas para concluir a imagem para os poucos que têm um UIWebView:

UIWebView - Struts: superior, esquerda, direita Molas: ambas

UIButton - nada definido, ou seja, flexível em qualquer lugar

Embora minha lâmpada esteja fraca, parece haver esperança.


Por favor, tenha paciência comigo, porque eu precisava de mais espaço do que o previsto para uma resposta curta.

Obrigado por tentar entender o que eu realmente estou pescando ... então aqui vai.

1) Cada UIViewController (um aplicativo TabBar) possui uma UIImage, algum texto e o que estiver no topo. Outro denominador comum é um UIButton na parte inferior. Em alguns dos UIViewControllers, tenho um UIWebView acima do UIButton.

Então, UIImage, texto etc. UIWebView (em ALGUNS) UIButton

Ao redor de todo o exposto, há um UIScrollView.

2) Para aqueles que possuem um UIWebView, seu autoresizingMask se parece com:

   
   |
   

   ^
   |
   |

| - | ← ---- → | - | | | V A máscara do UIButton não tem nada definido, ou seja, flexível em qualquer lugar

No meu -viewDidLoad, chamo meu -repositionSubViews no qual faço o seguinte:

Se não houver UIWebView, não faço nada, exceto centralizar o UIButton que coloquei com o IB.

Se eu tiver um UIWebView, determinarei sua altura * content * e defino seu quadro para incluir todo o conteúdo.

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

Depois de fazer isso, empurro programaticamente o UIButton para baixo, para que ele fique abaixo do UIWebView.

Tudo funciona, até eu girá-lo de Retrato para Paisagem.

Eu chamo meu -repositionSubViews dentro do meu -didRotateFromInterfaceOrientation.

Por que a altura do conteúdo do meu UIWebView não muda com a rotação?

De Retrato para Paisagem, a largura do conteúdo deve expandir e a altura do conteúdo deve diminuir. Faz visualmente como deveria, mas não de acordo com o meu NSLog.

De qualquer forma, com ou sem um UIWebView, o botão sobre o qual eu falei se move abaixo da Barra de Tabulação no modo Paisagem, mas ele não rola para cima para ser visto. Eu o vejo por trás da TabBar quando rolar "vigorosamente", mas depois "retrocede" por trás da TabBar.

Resumindo, esse último é o motivo pelo qual perguntei sobre a altura do TabBar e do NavigationBar porque o TabBar se planta na parte inferior do UIView e o NavigationBar empurra o UIView para baixo.

Agora, adicionarei um ou dois comentários aqui, porque eles não teriam sentido antes.

Sem o UIWebView, deixo tudo como é visto pelo IB.

Com um UIWebView, aumento o frame.height do UIWebView para o contentHeight e também ajusto a altura do UIScrollView circundante que envolve todas as sub-visualizações.

Bem, aí está.

Respostas:


258

Faça algo assim?

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

A versão rápida está localizada aqui


ATUALIZAR

iOS 13

Como o statusBarFramefoi reprovado, iOS13você pode usar o seguinte:

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

Obrigado, obrigado. MAS, agora que você resolveu esse problema, como determino se o NavigationController, também conhecido como MoreViewController, está sendo exibido no meu aplicativo da barra de guias.

Não entendi direito o que você está tentando dizer. Você está tentando descobrir se o MoreViewController está aparecendo? (Se sim, o que você quer fazer?) E, se não, poderia esclarecer o que exatamente você quer dizer?
Sum

Li sua pergunta novamente e ainda não entendi o que você quer fazer? Se você quiser fazer algo quando a visualização aparecer, insira o código em viewDidAppear. Se você esclarecer um pouco, seria mais fácil descobrir o que você quer fazer.
Sum

2
Sei que isso não aborda a pergunta que ele fez, mas parece que ele estava olhando para ver se a barra de navegação estava oculta ou não. Se assim for, ele poderia ter usado self.navigationController.navigationBarHidden esperança totalmente isso ajude alguém :)
taylorcressy

2
Em iOS 7+ pode ser necessário também considerar o 20px barra de status, dependendo da aplicação
Slayter

94

Com o iPhone-X, a altura da barra superior (barra de navegação + barra de status) é alterada (aumentada).

Tente isso se desejar a altura exata da barra superior (barra de navegação + barra de status):

ATUALIZAR

iOS 13

Como o statusBarFramefoi reprovado, iOS13você pode usar o seguinte:

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

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

Para facilitar, tente esta extensão 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)
    }
}

Swift 3

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


3
A resposta do Swift 4 retorna uma altura 0 para mim.
Chewie The Chorkie

61

Versão rápida :

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

Sempre retorna 44. Como saber o tamanho recolhido (44) ou o tamanho expandido (títulos grandes) ??? Dinamicamente, é claro. (Eu sei o que mede)
Markus

6

Você tentou isso?

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

5

Suporte iOS 13 e abaixo:

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
        }
    }
}

4
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 );
*/

Então, tudo o que realmente precisamos é

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;

2

Meu aplicativo possui algumas visualizações que exigiam uma barra de navegação personalizada na interface do usuário para a aparência, porém sem o controlador de navegação. E o aplicativo é necessário para oferecer suporte à versão iOS anterior ao iOS 11, para que o guia prático de layout de área segura não possa ser usado, e eu tenho que ajustar a posição e a altura da barra de navegação programaticamente.

Anexei a barra de navegação à sua visão geral diretamente, pulando o guia de layout da área segura, como mencionado acima. E a altura da barra de status pode ser recuperada facilmente do UIApplication, mas a altura padrão da barra de navegação é realmente um problema ...

Fiquei impressionado por quase meia noite, com várias pesquisas e testes, até que finalmente recebi a dica de outro post (embora não funcionasse para mim), que você poderia realmente obter a altura de UIView.sizeThatFits (), assim: :

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

    [super viewWillLayoutSubviews];    
}

Finalmente, uma barra de navegação perfeita parecendo exatamente a mesma que a embutida!


2

Se você deseja obter navigationBarapenas a altura, é simples:

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

No entanto, se você precisar da altura do topo do iPhone, não precisará obter a navigationBaraltura e adicioná-la à statusBaraltura, basta ligar safeAreaInsetspara isso.

self.view.safeAreaInsets.top

1

A lâmpada começou a acender. Infelizmente, não descobri uma maneira uniforme de corrigir o problema, conforme descrito abaixo.

Acredito que todo o meu problema esteja centrado nas minhas autoresizingMasks. E a razão pela qual concluí que existem os mesmos sintomas, com ou sem um UIWebView. E esse sintoma é que tudo é pêssego para o Portrait. Para Paisagem, o UIButton mais abaixo aparece atrás da TabBar.

Por exemplo, em um UIView, eu tenho, de cima para baixo:

UIView - ambas as molas definidas (caso padrão) e sem suportes

UIScrollView - Se eu definir as duas molas e limpar tudo o resto (como o UIView), o UIButton entrará no objeto imediatamente acima dele. Se eu limpar tudo, o UIButton está OK, mas as coisas na parte superior se escondem atrás da Configuração da barra de status, apenas a parte superior, o UIButton aparece atrás da barra de guias.

UILabel e UIImage a seguir na vertical - conjunto superior de suporte, flexível em qualquer outro lugar

Apenas para concluir a imagem para os poucos que têm um UIWebView:

UIWebView - Struts: superior, esquerda, direita Molas: ambas

UIButton - nada definido, ou seja, flexível em qualquer lugar

Embora minha lâmpada esteja fraca, parece haver esperança.


Defina o suporte superior e defina as duas molas do UIWebView - problema resolvido e obrigado cachos. Por uma questão de fato, meu único problema restante é projetar os gráficos amaldiçoados @ 2X etc. Eu uso o GraphicConverter de uma maneira muito simples e funciona muito bem no design de páginas da Web. Mas todas essas coisas de 30px, 57px - estou entrando em território desconhecido para mim, pelo menos.

1

Aqui está o começo da minha resposta à sua atualização:

Por que a altura do conteúdo do meu UIWebView não muda com a rotação?

Poderia ser porque o redimensionamento automático não possui a autoresizingMask para todas as direções?

Outra sugestão antes de eu voltar para isso, você pode usar uma barra de ferramentas para suas necessidades. É um pouco mais simples, sempre estará na parte inferior, gira / posiciona automaticamente. Você pode ocultar / mostrar à vontade, etc. Assim: http://cdn.artoftheiphone.com/wp-content/uploads/2009/01/yellow-pages-iphone-app-2.jpg

Você pode ter olhado para essa opção, mas apenas lançando-a por aí.

Outra idéia: você poderia detectar de que orientação está girando e basta colocar o botão programaticamente para ajustar a barra de guias. (Isso é possível com o código)


Depois de duas semanas resolvendo isso, decidi que o "problema" era mais difícil, colocando um UIButton ABAIXO da subvisualização UIWebView. Então, eu coloquei o botão ACIMA. Francamente, simplesmente não parece "certo" dessa maneira ... mas agora funciona SORT OF. ABAIXO ou ACIMA, o UIWebView definiu o suporte superior e apenas a mola horizontal. BTW, é o contentHeight do UIWebView que não muda com a rotação.

0

Eu tenho usado:

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

Funcionando muito bem se você deseja obter a altura da barra de navegação E sua origem Y.


0

Swift 5

Se você também deseja considerar o safeArea:

 let height = navigationController?.navigationBar.frame.maxY  

0

Handy Swift 4, caso seja útil para outra pessoa. Funciona mesmo se o controlador de exibição atual não exibir uma barra de navegação.

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
  }
}

Uso:

UINavigationController.navBarHeight()
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.