Imitar o Facebook ocultar / mostrar expansão / contratação Barra de Navegação

128

No novo aplicativo para iPhone iOS7 do Facebook, quando o usuário rola a tela, navigationBaroculta-se gradualmente até um ponto em que desaparece completamente. Então, quando o usuário rola para baixo, ele navigationBarse mostra gradualmente.

Como você implementaria esse comportamento? Estou ciente da seguinte solução, mas ela desaparece imediatamente e não está ligada à velocidade do gesto de rolagem do usuário.

[navigationController setNavigationBarHidden: YES animated:YES];

Espero que isso não seja duplicado, pois não tenho certeza da melhor maneira de descrever o comportamento de "expansão / contratação".

El Mocoso
fonte
2
Mesmas questões: stackoverflow.com/questions/21929220/... Note que é incrivelmente difícil para absolutamente coincidir com o comportamento Safari. Existem algumas regras muito, muito complicadas lá!
Fattie
1
No meu projeto, usei esse projeto e funcionou muito bem. Dê uma olhada em sua documentação.
Vinicius
O github.com/bryankeller/BLKFlexibleHeightBar permitirá que você faça o que deseja e muito mais. Permite especificar exatamente como a barra fica em cada estágio de sua transição, de maximizada para minimizada. Ele ainda permite que você especifique seus próprios comportamentos, para que ele possa agir como Safari, Facebook ou algum outro aplicativo.
blkhp19
Eu não usei uma barra de navegação, mas adicionei uma visão geral. A visualização que replica a barra de navegação será expandida e contrairá com base na rolagem. Eu usei o método delegado scrollViewDidScroll para realizar a tarefa. Você pode querer verificar e executar o código fonte abaixo .. dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Respostas:

162

A solução dada por @peerless é um ótimo começo, mas só inicia uma animação sempre que o arrastar começa, sem considerar a velocidade do pergaminho. Isso resulta em uma experiência mais picante do que no aplicativo do Facebook. Para corresponder ao comportamento do Facebook, precisamos:

  • ocultar / mostrar a barra de navegação a uma taxa proporcional à taxa de arrasto
  • inicie uma animação para ocultar completamente a barra se a rolagem parar quando a barra estiver parcialmente oculta
  • esmaecer os itens da barra de navegação quando a barra diminuir.

Primeiro, você precisará da seguinte propriedade:

@property (nonatomic) CGFloat previousScrollViewYOffset;

E aqui estão os UIScrollViewDelegatemétodos:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGRect frame = self.navigationController.navigationBar.frame;
    CGFloat size = frame.size.height - 21;
    CGFloat framePercentageHidden = ((20 - frame.origin.y) / (frame.size.height - 1));
    CGFloat scrollOffset = scrollView.contentOffset.y;
    CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
    CGFloat scrollHeight = scrollView.frame.size.height;
    CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;

    if (scrollOffset <= -scrollView.contentInset.top) {
        frame.origin.y = 20;
    } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
        frame.origin.y = -size;
    } else {
        frame.origin.y = MIN(20, MAX(-size, frame.origin.y - scrollDiff));
    }

    [self.navigationController.navigationBar setFrame:frame];
    [self updateBarButtonItems:(1 - framePercentageHidden)];
    self.previousScrollViewYOffset = scrollOffset;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
                  willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

Você também precisará destes métodos auxiliares:

- (void)stoppedScrolling
{
    CGRect frame = self.navigationController.navigationBar.frame;
    if (frame.origin.y < 20) {
        [self animateNavBarTo:-(frame.size.height - 21)];
    }
}

- (void)updateBarButtonItems:(CGFloat)alpha
{
    [self.navigationItem.leftBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
        item.customView.alpha = alpha;
    }];
    [self.navigationItem.rightBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
        item.customView.alpha = alpha;
    }];
    self.navigationItem.titleView.alpha = alpha;
    self.navigationController.navigationBar.tintColor = [self.navigationController.navigationBar.tintColor colorWithAlphaComponent:alpha];
}

- (void)animateNavBarTo:(CGFloat)y
{
    [UIView animateWithDuration:0.2 animations:^{
        CGRect frame = self.navigationController.navigationBar.frame;
        CGFloat alpha = (frame.origin.y >= y ? 0 : 1);
        frame.origin.y = y;
        [self.navigationController.navigationBar setFrame:frame];
        [self updateBarButtonItems:alpha];
    }];
}

Para um comportamento ligeiramente diferente, substitua a linha que reposiciona a barra ao rolar (a entrada do elsebloco scrollViewDidScroll) por esta:

frame.origin.y = MIN(20, 
                     MAX(-size, frame.origin.y - 
                               (frame.size.height * (scrollDiff / scrollHeight))));

Isso posiciona a barra com base na última porcentagem de rolagem, em vez de uma quantidade absoluta, o que resulta em um desbotamento mais lento. O comportamento original é mais parecido com o Facebook, mas eu também gosto desse.

Nota: Esta solução é apenas para iOS 7 ou superior. Certifique-se de adicionar as verificações necessárias se você estiver suportando versões mais antigas do iOS.

Wayne
fonte
Isso funciona. Exceto que você esteja assumindo que os itens do botão de barra tenham visualizações personalizadas. No meu caso, não tenho uma visualização personalizada. Portanto, o acima não oculta os botões da barra. Acho solução @peerless é melhor para itens escondidos e mostrando navbar
Dhanush
1
Você está certo. Eu posso procurar uma solução mais geral neste fim de semana. Não deve ser muito difícil segmentar os itens padrão em vez de customView.
Wayne
Não resolvi o problema de @ Dhanush, mas tenho uma atualização.
Wayne Wayne
1
Corrigi o problema com os botões da barra de ações, mas esse código tem outro problema. Se ScrollView's contentSizeé menor do quadro, a animação de deslizamento não funciona. Além disso, redefina o alfa de todos os itens de navegação para 1,0 pol viewDidDisappear.
Legoless
1
Você checou esta solução em controladores usados ​​com AutoLayout? No meu caso tudo está funcionando bem, sem AutoLayout, mas quando se ligar, ele ainda ser visível alguma faixa branca estranha sob ele
Aliaksandr B.
52

EDIT: Apenas para iOS 8 e superior.

Você pode tentar usar

self.navigationController.hidesBarsOnSwipe = YES;

Funciona para mim.

Se a sua codificação for rápida, você deverá usar desta maneira (em https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true
Pedro Romão
fonte
isso não está disponível no iOS <8.0
Petar
1
Como pet60t0 disse, e peço desculpas, só funciona para iOS 8 e superior.
Pedro Romão
4
Como você traz de volta depois disso?
C0D3 29/04
o comportamento é um pouco estranho. Não explorei muito, mas se você rolar mais rápido, será exibido novamente.
Pedro Romão
@ PedroRomão ótima resposta.
Gagan_iOS
43

Aqui está mais uma implementação: TLYShyNavBar v1.0.0 lançado!

Decidi fazer o meu próprio depois de experimentar as soluções fornecidas e, para mim, eles estavam com um desempenho ruim, tinham uma alta barreira de entrada e código da placa da caldeira ou faltavam a visualização da extensão abaixo da barra de navegação. Para usar esse componente, tudo que você precisa fazer é:

self.shyNavBarManager.scrollView = self.scrollView;

Ah, e é testado em batalha em nosso próprio aplicativo.

Mazyod
fonte
@TimArnold Obrigado pelo seu feedback! Corrigi esse problema, mas ainda não atualizei o pod> _ <fará isso agora! .. Pod atualizado!
Mazyod
Vou tentar outra vez! Obrigado!
precisa
Eu aprecio sua ajuda. Parece que seu commit ajudou. Ainda estou tendo um problema estranho em que meu UICollectionView não é redimensionado corretamente como a barra de navegação e, portanto, as células subindo onde a barra de navegação USADA deveria ser cortada, pois estão fora dos limites da exibição de coleção. Você sabe por que isso pode estar acontecendo?
precisa
4
Figuras: encontrei minha solução segundos depois de escrever este comentário. Eu tinha que ter certeza extendedLayoutIncludesOpaqueBarsfoi definida como YESna minhaUICollectionViewController
Tim Camber
@TimArnold Isso é incrível! Havia outro cara com o mesmo problema , e esperamos que sua solução o ajude.
Mazyod
33

Você pode dar uma olhada no meu GTScrollNavigationBar . Eu subclassifiquei UINavigationBar para fazer rolagem com base na rolagem de um UIScrollView.

Nota: Se você tiver uma barra de navegação OPAQUE, a visualização de rolagem deve EXPANDIR à medida que a barra de navegação fica ESCONDIDA. É exatamente isso que o GTScrollNavigationBar faz. (Assim como, por exemplo, Safari no iOS.)

Thuy
fonte
BTW para quem está lendo, exatamente como chamar initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie
@ Thuy homem ótimo trabalho! Então, eu tenho isso funcionando perfeitamente em um controlador de exibição de tabela, exceto uma coisa ... Eu tenho que puxar para atualizar implementado na parte superior. Fica estranho ao tentar puxar para baixo e atualizar. Pode haver uma solução alternativa para isso?
Aherrick 17/10
@ Thuy também ... digamos que eu tinha um controlador de exibição em que implementei uma exibição de tabela na parte inferior. Desejo conectar-me à visualização da tabela que funciona, no entanto, tenho outra visualização acima da visualização da tabela que também quero desaparecer. Como isso pode funcionar?
Aherrick 17/10
25

O iOS8 inclui propriedades para ocultar a barra de navegação gratuitamente. Existe um vídeo da WWDC que o demonstra, procure por "Ver avanços do controlador no iOS 8".

Exemplo :

class QuotesTableViewController: UITableViewController {

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    navigationController?.hidesBarsOnSwipe = true
}

}

Outras propriedades:

class UINavigationController : UIViewController {

    //... truncated

    /// When the keyboard appears, the navigation controller's navigationBar toolbar will be hidden. The bars will remain hidden when the keyboard dismisses, but a tap in the content area will show them.
    @availability(iOS, introduced=8.0)
    var hidesBarsWhenKeyboardAppears: Bool
    /// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items.
    @availability(iOS, introduced=8.0)
    var hidesBarsOnSwipe: Bool
    /// The gesture recognizer that triggers if the bars will hide or show due to a swipe. Do not change the delegate or attempt to replace this gesture by overriding this method.
    @availability(iOS, introduced=8.0)
    var barHideOnSwipeGestureRecognizer: UIPanGestureRecognizer { get }
    /// When the UINavigationController's vertical size class is compact, hide the UINavigationBar and UIToolbar. Unhandled taps in the regions that would normally be occupied by these bars will reveal the bars.
    @availability(iOS, introduced=8.0)
    var hidesBarsWhenVerticallyCompact: Bool
    /// When the user taps, the navigation controller's navigationBar & toolbar will be hidden or shown, depending on the hidden state of the navigationBar. The toolbar will only be shown if it has items to display.
    @availability(iOS, introduced=8.0)
    var hidesBarsOnTap: Bool
    /// The gesture recognizer used to recognize if the bars will hide or show due to a tap in content. Do not change the delegate or attempt to replace this gesture by overriding this method.
    @availability(iOS, introduced=8.0)
    unowned(unsafe) var barHideOnTapGestureRecognizer: UITapGestureRecognizer { get }
}

Encontrado em http://natashatherobot.com/navigation-bar-interactions-ios8/

Michael Peterson
fonte
12

Eu tenho algum tipo de solução rápida e suja para isso. Não foram realizados testes detalhados, mas aqui está a ideia:

Essa propriedade manterá todos os itens na barra de navegação da minha classe UITableViewController

@property (strong, nonatomic) NSArray *navBarItems;

Na mesma classe UITableViewController eu tenho:

-(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;

    if(self.navBarItems.count > 0){
        [self.navigationController.navigationBar setItems:self.navBarItems];
    }

    [self.navigationController.navigationBar setFrame:frame];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    CGFloat size = frame.size.height - 21;

    if([scrollView.panGestureRecognizer translationInView:self.view].y < 0)
    {
        frame.origin.y = -size;

        if(self.navigationController.navigationBar.items.count > 0){
            self.navBarItems = [self.navigationController.navigationBar.items copy];
            [self.navigationController.navigationBar setItems:nil];
        }
    }
    else if([scrollView.panGestureRecognizer translationInView:self.view].y > 0)
    {
        frame.origin.y = 20;

        if(self.navBarItems.count > 0){
            [self.navigationController.navigationBar setItems:self.navBarItems];
        }
    }

    [UIView beginAnimations:@"toggleNavBar" context:nil];
    [UIView setAnimationDuration:0.2];
    [self.navigationController.navigationBar setFrame:frame];
    [UIView commitAnimations];
}

Isso é apenas para ios> = 7, é feio eu sei, mas é uma maneira rápida de conseguir isso. Quaisquer comentários / sugestões são bem-vindos :)

inigualável
fonte
12

Isso funciona para iOS 8 e superior e garante que a barra de status ainda retenha seu plano de fundo

self.navigationController.hidesBarsOnSwipe = YES;
CGRect statuBarFrame = [UIApplication sharedApplication].statusBarFrame;
UIView *statusbarBg = [[UIView alloc] initWithFrame:statuBarFrame];
statusbarBg.backgroundColor = [UIColor blackColor];
[self.navigationController.view addSubview:statusbarBg];

E se você quiser mostrar a barra de navegação ao tocar na barra de status, faça o seguinte:

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
     self.navigationController.navigationBarHidden = NO;
}
Zhong Huiwen
fonte
10

Aqui está minha implementação: SherginScrollableNavigationBar .

Na minha abordagem, estou usando KVOpara observar UIScrollViewo estado, portanto, não há necessidade de usar um delegado (e você pode usá-lo para o que precisar).

Valentin Shergin
fonte
Para sua informação, isso nem sempre funciona corretamente. Eu tentei isso também e funciona desde que você não esteja "pulando" a visualização de rolagem. Parece que o KVO não é acionado quando está na parte de rejeição. A chamada de delegado para o contentOffset é acionada.
Joris Mans
7

Por favor, tente esta minha solução e deixe-me saber por que isso não é tão bom quanto as respostas anteriores.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    if (fabs(velocity.y) > 1)
        [self hideTopBar:(velocity.y > 0)];
}

- (void)hideTopBar:(BOOL)hide
{
    [self.navigationController setNavigationBarHidden:hide animated:YES];
    [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide];
}
Nishant
fonte
1
Fiz algo semelhante a isso, e essa é facilmente a melhor solução. Eu tentei vários hacks e bibliotecas, e este é o único que funciona no iOS 9 com um tableView que não cobre a tela inteira. Parabéns!
Skinsell
6

Uma maneira de conseguir isso é o seguinte.

Registre seu controlador de exibição como o UIScrollViewDelegateseu, UITableViewpor exemplo.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

De dentro dos UIScrollViewDelegatemétodos, você pode obter o novo contentOffset e traduzir seuUINavigationBar cima ou para baixo de acordo.

A configuração do alfa das subvisões também pode ser feita com base em alguns valores de limite e fatores que você pode definir e calcular.

Espero que ajude!

Diana Sule
fonte
Encontrado post semelhante aqui
Diana Sule
Obrigado Diana! Suspeitei que talvez fosse necessário implementar os métodos UIScrollViewDelegate, mas parecia que isso seria um pouco exagerado. Depois que eu descobrir isso, postarei. Felicidades!
El Mocoso
4

Além da resposta de Iwburk, adicionei o seguinte para corrigir o problema alfa em barras de navegação não personalizadas e redefinir a barra de navegação no método viewWillDisappear:

- (void)updateBarButtonItems:(CGFloat)alpha
{
    for (UIView *view in self.navigationController.navigationBar.subviews) {
        NSString *className = NSStringFromClass([view class]);

        if ( ![className isEqualToString:@"_UINavigationBarBackground"] ) {
            view.alpha = alpha;
        }
    }
}

- (void)resetNavigationBar {
    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;
    [self.navigationController.navigationBar setFrame:frame];
    [self updateBarButtonItems:1.0f];
}
gelo azul
fonte
Existe outra maneira de fazer um loop pelas subvisões?
thisiscrazy4
Pelo que pude dizer em uma barra de navegação não personalizada, existem 4 subvisões totais: _UINavigationBarBackground, UINavigationItemView, UINavigationItemButtonView e _UINavigationBarBackIndicatorView. O loop é bem rápido e parece não afetar o desempenho do meu aplicativo.
blueice
Como _UINavigationBarBackground sempre parece ser a primeira subvisão, você pode acessar o restante diretamente: ((UIView *) self.navigationController.navigationBar.subviews [1]). Alpha = alpha;
blueice
4

Eu estava procurando uma solução que permitisse qualquer estilo e comportamento. Você notará que o comportamento de condensação da barra é diferente em muitos aplicativos diferentes. E, claro, a aparência da barra é totalmente diferente entre os aplicativos.

Criei uma solução para esse problema com https://github.com/bryankeller/BLKFlexibleHeightBar/

Você pode definir suas próprias regras de comportamento para controlar como e quando a barra diminui e cresce, e pode definir exatamente como deseja que as subvisões da barra reajam à condensação ou crescimento da barra.

Dê uma olhada no meu projeto, se você quiser muita flexibilidade para criar qualquer tipo de barra de cabeçalho que possa imaginar.

blkhp19
fonte
Como posso adicionar um botão a este customHeaderView que será oculto quando eu rolar para cima.Não preciso de um botão estático.É possível? Tentei criar um botão como uma subvisão.Mas não está recebendo nenhum toque.
abhimuralidharan
3

Eu estava tentando emular esse comportamento em uma situação em que precisava de um cabeçalho personalizado sobre um UITableView. Eu rolei minha própria barra de "navegação" porque esta fica abaixo de várias outras coisas na página e eu queria que os cabeçalhos das seções seguissem o comportamento padrão de "encaixe". Acho que encontrei uma maneira bastante inteligente e sucinta de ajustar um UITableView / UIScrollView junto com outro objeto em um estilo semelhante ao visto no Facebook / Instagram / Chrome / etc. aplicativos.

No meu arquivo .xib, tenho meus componentes carregados em uma exibição de forma livre: http://imgur.com/0z9yebJ (desculpe, não tenha o representante para imagens embutidas)

Observe que, na barra lateral esquerda, a tabela está ordenada atrás da visualização principal do cabeçalho. Você não pode distinguir da captura de tela, mas ela também tem a mesma posição y da visualização principal do cabeçalho. Como se estende para fora da vista, a propriedade contentInset no UITableView é definida como 76 (a altura da exibição principal do cabeçalho).

Para fazer com que a visualização do cabeçalho principal deslize para cima em uníssono com o UIScrollView, eu uso os métodos scrollViewDidScroll do UIScrollViewDelegate para executar alguns cálculos e alterar o contentInset do UIScrollView, bem como o quadro da visualização do cabeçalho principal.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    UIEdgeInsets insets = scrollView.contentInset;
    //tableViewInsetDelta and tableViewOriginalInsetValue are NSInteger variables that I set to 0 and 76, respectively, in viewDidLoad
    tableViewInsetDelta = tableViewOriginalInsetValue + scrollView.contentOffset.y;
    insets.top = tableViewOriginalInsetValue - tableViewInsetDelta;

    if (scrollView.contentOffset.y > -76 && scrollView.contentOffset.y < 0) {
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44 - tableViewInsetDelta, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y > 0) {
        insets.top = 0;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, -32, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y < -76) {
        insets.top = 76;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    }
}

A primeira instrução if faz a maior parte do trabalho pesado, mas eu tive que incluir as outras duas para lidar com situações em que o usuário está arrastando com força e os valores contentOffset iniciais enviados para scrollViewDidScroll estão fora do intervalo da primeira instrução if .

Em última análise, isso está funcionando muito bem para mim. Eu odeio carregar meus projetos com um monte de subclasses inchadas. Não posso falar se essa é a melhor solução em termos de desempenho (sempre hesitei em colocar qualquer código no scrollViewDidScroll desde que ele é chamado o tempo todo), mas a pegada de código é a menor que já vi em qualquer solução para esse problema e não envolve o aninhamento de um UITableView em um UIScrollView (a Apple recomenda isso na documentação e os eventos de toque acabam um pouco estranhos no UITableView). Espero que isso ajude alguém!

Brian
fonte
3

HidingNavigationBar, um ótimo projeto que oculta a barra de navegação e a barra de guias, se você desejar.

HidingNavigationBar suporta ocultar / mostrar os seguintes elementos de exibição:

UINavigationBar

UINavigationBar e uma extensão UIView

UINavigationBar e uma UIToolbar

UINavigationBar e um UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar

Michael Peterson
fonte
2

Tentei implementar o GTScrollNavigationBar, mas meu aplicativo exigia que eu modificasse as restrições de layout automático. Decidi colocar um exemplo da minha implementação no GitHub, caso alguém mais tenha que fazer isso com o layout automático. O outro problema que tive com a maioria das outras implementações é que as pessoas não definem os limites da exibição de rolagem para evitar o efeito de rolagem de paralaxe que você cria enquanto rola e ajusta o tamanho da exibição de rolagem simultaneamente.

Confira JSCollapsingNavBarViewController se você precisar fazer isso com o layout automático. Incluí duas versões, uma apenas com a barra de navegação e outra com uma sub-barra abaixo da barra de navegação que é recolhida antes de ser recolhida.

jwswart
fonte
1

Eu tentei dessa maneira, espero que ajude. basta implementar o código no método delegado e definir para a visualização / subvisualização desejada

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{ 
            CGRect frame=self.view.frame;
            CGRect resultFrame=CGRectZero;
            if(scrollView.contentOffset.y==0 || scrollView.contentOffset.y<0){
                self.lastContentOffset=0;
                self.offset=0;
                resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                [self showHide:YES withFrame:resultFrame];
            }else if (self.lastContentOffset > scrollView.contentOffset.y){
                NSNumber *temp=[NSNumber numberWithDouble:self.lastContentOffset-scrollView.contentOffset.y];
                if(temp.intValue>40 || self.offset.intValue<temp.intValue){
                    self.offset=[NSNumber numberWithInt:0];
                    resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                    [self showHide:YES withFrame:resultFrame];
                }else{
                    if(temp.intValue>0){
                        self.offset=[NSNumber numberWithInt:self.offset.intValue-temp.intValue];
                        resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                        [self showHide:YES withFrame:resultFrame];
                    }
                }
            }else if (self.lastContentOffset < scrollView.contentOffset.y){
                NSNumber *temp=[NSNumber numberWithDouble:scrollView.contentOffset.y-self.lastContentOffset];
                if(self.offset.intValue>40 || (self.offset.intValue+temp.intValue)>40){
                    self.offset=[NSNumber numberWithInt:40];
    // Pass the resultFrame
                    [self showHide:NO withFrame:resultFrame];
                }else{
                    self.offset=[NSNumber numberWithInt:self.offset.intValue+temp.intValue];
                    resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                    [self showHide:YES withFrame:resultFrame];
                }
            }
            self.lastContentOffset = scrollView.contentOffset.y;

        }

-(void)showHide:(Boolean)boolView withFrame:(CGRect)frame{
               if(showSRPFilter){
                        //Assign value of "frame"to any view on which you wan to to perform animation
                }else{
                       //Assign value of "frame"to any view on which you wan to to perform animation
                }
        }
user2968901
fonte
1

Uma extensão da resposta de @Iwburk ... Em vez de alterar a origem da barra de navegação, eu precisava expandir / reduzir o tamanho da barra de navegação.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGRect frame = self.previousRect; // a property set in the init method to hold the initial size of the uinavigationbar
    CGFloat size = frame.size.height;
    CGFloat framePercentageHidden = ((MINIMUMNAVBARHEIGHT - frame.origin.y) / (frame.size.height - 1));
    CGFloat scrollOffset = scrollView.contentOffset.y;
    CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
    CGFloat scrollHeight = scrollView.frame.size.height;
    CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;

    if (scrollOffset <= -scrollView.contentInset.top) {
        frame.origin.y = -MINIMUMNAVBARHEIGHT;
    } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
        frame.origin.y = -size;
    } else {
        frame.origin.y = MIN(-MINIMUMNAVBARHEIGHT, MAX(-size, frame.origin.y - scrollDiff));
    }

    self.previousRect = CGRectMake(0, frame.origin.y, self.jsExtendedBarView.frame.size.width, 155);
    self.layoutConstraintExtendedViewHeight.constant = MAXIMUMNAVBARHEIGHT + frame.origin.y + MINIMUMNAVBARHEIGHT;
    [self updateBarButtonItems:(1 - framePercentageHidden)];
    self.previousScrollViewYOffset = scrollOffset;
}

Ainda não funciona com o stoppedScrollingmétodo, vou postar uma atualização quando a tiver

jsetting32
fonte
0

Todas essas abordagens parecem muito complicadas ... Então, naturalmente, eu criei a minha:

class ViewController: UIViewController, UIScrollViewDelegate {
    var originalNavbarHeight:CGFloat = 0.0
    var minimumNavbarHeight:CGFloat = 0
    weak var scrollView:UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // setup delegates 
        scrollView.delegate = self
        // save the original nav bar height
        originalNavbarHeight = navigationController!.navigationBar.height
    }


    func scrollViewDidScroll(scrollView: UIScrollView) {
        // will relayout subviews
        view.setNeedsLayout() // calls viewDidLayoutSubviews
    }

    override func viewDidLayoutSubviews() {
        var percentageScrolled = min(scrollView.contentOffset.y / originalNavbarHeight, 1)
        navigationController?.navigationBar.height = min(max((1 - percentageScrolled) * originalNavbarHeight, minimumNavbarHeight), originalNavbarHeight)
        // re-position and scale scrollview
        scrollView.y = navigationController!.navigationBar.height + UIApplication.sharedApplication().statusBarFrame.height
        scrollView.height = view.height - scrollView.y
    }

    override func viewWillDisappear(animated: Bool) {
        navigationController?.navigationBar.height = originalNavbarHeight
    }

}
Oxcug
fonte
navigationBar.height? scrollView.height? Você usa uma extensão na framepropriedade?
Ely
0

Encontrei todas as respostas dadas no Objective-C. Esta é a minha resposta no Swift 3. Este é um código muito genérico e pode ser usado diretamente. Ele funciona com UIScrollView e UITableView.

var lastContentOffset: CGPoint? = nil
var maxMinus: CGFloat           = -24.0
var maxPlus: CGFloat            = 20.0
var initial: CGFloat            = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    self.title = "Alarm Details"
    self.lastContentOffset = self.alarmDetailsTableView.contentOffset
    initial = maxPlus
}

func scrollViewDidScroll(_ scrollView: UIScrollView)
{
    var navigationBarFrame: CGRect   = self.navigationController!.navigationBar.frame
    let currentOffset = scrollView.contentOffset

    if (currentOffset.y > (self.lastContentOffset?.y)!) {
        if currentOffset.y > 0 {
            initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
        else if scrollView.contentSize.height < scrollView.frame.size.height {
            initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
    }
    else {
        if currentOffset.y < scrollView.contentSize.height - scrollView.frame.size.height {
            initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
        else if scrollView.contentSize.height < scrollView.frame.size.height && initial < maxPlus {
            initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
    }

    initial = (initial <= maxMinus) ? maxMinus : initial
    initial = (initial >= maxPlus) ? maxPlus : initial

    navigationBarFrame.origin.y = initial

    self.navigationController!.navigationBar.frame = navigationBarFrame
    scrollView.frame = CGRect(x: 0.0, y: initial + navigationBarFrame.size.height , width: navigationBarFrame.size.width, height: self.view.frame.size.height - (initial + navigationBarFrame.size.height))

    let framePercentageHidden: CGFloat              = ((20 - navigationBarFrame.origin.y) / (navigationBarFrame.size.height));
    self.lastContentOffset                          = currentOffset;
    self.updateBarButtonItems(alpha: 1 - framePercentageHidden)
}

func updateBarButtonItems(alpha: CGFloat)
{
    self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.darkGray.withAlphaComponent(alpha)]
    self.navigationController?.navigationBar.isUserInteractionEnabled = (alpha < 1) ? false: true

    guard (self.navigationItem.leftBarButtonItems?.count) != nil else { return }

    for (_, value) in self.navigationItem.leftBarButtonItems!.enumerated() {
        value.customView?.alpha = alpha
    }

    guard (self.navigationItem.rightBarButtonItems?.count) != nil else { return }

    for (_, value) in (self.navigationItem.rightBarButtonItems?.enumerated())! {
        value.customView?.alpha = alpha
    }
}

A lógica de definir alfa para itens de navegação é copiada da resposta @ WayneBurkett e reescrita no Swift 3.

Dev
fonte