Imitar Facebook ocultar / mostrar expandir / contraer la barra de navegación

128

En la nueva aplicación para iPhone iOS7 de Facebook, cuando el usuario se desplaza hacia arriba, navigationBarse oculta gradualmente hasta el punto en que desaparece por completo. Luego, cuando el usuario se desplaza hacia abajo, navigationBarse muestra gradualmente.

¿Cómo implementaría usted mismo este comportamiento? Soy consciente de la siguiente solución, pero desaparece de inmediato y no está vinculada a la velocidad del gesto de desplazamiento del usuario.

[navigationController setNavigationBarHidden: YES animated:YES];

Espero que esto no sea un duplicado ya que no estoy seguro de cómo describir mejor el comportamiento de "expansión / contracción".

El Mocoso
fuente
2
Mismas cuestiones: stackoverflow.com/questions/21929220/... en cuenta que es muy difícil para absolutamente coinciden con el comportamiento de Safari. ¡Hay algunas reglas muy, muy complicadas allí!
Fattie
1
En mi proyecto utilicé este proyecto y funcionó bien. Echa un vistazo a su documentación.
Vinicius
github.com/bryankeller/BLKFlexibleHeightBar te permitirá hacer lo que quieras y más. Le permite especificar exactamente cómo se ve la barra en cada etapa de su transición de maximizado a minimizado. Incluso le permite especificar sus propios comportamientos, por lo que puede actuar como Safari, Facebook o alguna otra aplicación.
blkhp19
No utilicé una barra de navegación uina pero agregué una uiview en su lugar. La vista que replica la barra de navegación se expandirá y contraerá según el desplazamiento. Utilicé el método de delegado scrollViewDidScroll para lograr la tarea. Es posible que desee verificar y ejecutar el código fuente a continuación .. dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Respuestas:

162

La solución dada por @peerless es un gran comienzo, pero solo inicia una animación cada vez que comienza el arrastre, sin considerar la velocidad del desplazamiento. Esto da como resultado una experiencia más agitada que la que obtienes en la aplicación de Facebook. Para que coincida con el comportamiento de Facebook, necesitamos:

  • ocultar / mostrar la barra de navegación a una velocidad proporcional a la velocidad de arrastre
  • inicie una animación para ocultar completamente la barra si el desplazamiento se detiene cuando la barra está parcialmente oculta
  • desvanecen los elementos de la barra de navegación a medida que la barra se encoge.

Primero, necesitará la siguiente propiedad:

@property (nonatomic) CGFloat previousScrollViewYOffset;

Y aquí están los 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];
    }
}

También necesitará estos métodos de ayuda:

- (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 un comportamiento ligeramente diferente, reemplace la línea que reubica la barra al desplazarse (el elsebloque hacia adentro scrollViewDidScroll) con esta:

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

Esto posiciona la barra en función del último porcentaje de desplazamiento, en lugar de una cantidad absoluta, lo que resulta en un desvanecimiento más lento. El comportamiento original es más parecido a Facebook, pero también me gusta este.

Nota: Esta solución es solo para iOS 7+. Asegúrese de agregar las comprobaciones necesarias si admite versiones anteriores de iOS.

Wayne
fuente
Esto funciona. Excepto que está asumiendo que los elementos del botón de la barra tienen vistas personalizadas. En mi caso no tengo una vista personalizada. Entonces lo anterior no oculta los botones de la barra. Creo que la solución @peerless es mejor para ocultar y mostrar elementos de la barra de navegación
Dhanush
1
Tienes razón. Puedo buscar una solución más general este fin de semana. No debería ser demasiado difícil apuntar a los elementos predeterminados en lugar de customView.
Wayne
No he abordado el problema de @ Dhanush pero tengo una actualización.
Wayne
1
Solucioné el problema con los botones de la barra de valores, pero este código tiene otro problema. Si ScrollView's contentSizees más pequeño que el cuadro, la animación de deslizamiento no funciona. También asegúrese de restablecer todos los elementos alfa de navegación nuevamente a 1.0 pulg viewDidDisappear.
Legoless
1
¿Comprobó esta solución en los controladores que se utilizan AutoLayout? En mi caso, todo funciona bien sin AutoLayout, pero cuando se enciende, todavía se puede ver una extraña tira blanca debajo de él
Aliaksandr B.
52

EDITAR: solo para iOS 8 y superior.

Puedes intentar usar

self.navigationController.hidesBarsOnSwipe = YES;

Funciona para mi.

Si su codificación es rápida, debe usarla de esta manera (desde https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true
Pedro Romão
fuente
Esto no está disponible en iOS <8.0
Petar
1
Como dijo pet60t0, y me disculpo, solo funciona para iOS 8 y superior.
Pedro Romão
44
¿Cómo lo traes de vuelta después de eso?
C0D3
El comportamiento es un poco extraño. No exploré mucho, pero si te desplazas más rápido, se muestra de nuevo.
Pedro Romão
@ PedroRomão gran respuesta.
Gagan_iOS
43

Aquí hay una implementación más: ¡ TLYShyNavBar v1.0.0 lanzado!

Decidí hacer el mío después de probar las soluciones proporcionadas, y para mí, o estaban funcionando mal, tenían una alta barrera de entrada y un código de placa de caldera, o carecían de la vista de extensión debajo de la barra de navegación. Para usar este componente, todo lo que tiene que hacer es:

self.shyNavBarManager.scrollView = self.scrollView;

Ah, y está probado en batalla en nuestra propia aplicación.

Mazyod
fuente
@TimArnold ¡Gracias por tus comentarios! He solucionado ese problema, ¡pero todavía no actualicé el pod> _ <lo hará ahora mismo! .. Pod actualizado!
Mazyod
¡Le daré otra oportunidad! ¡Gracias!
Tim Camber
Aprecio tu ayuda. Parece que su compromiso ayudó. Todavía tengo un problema extraño en el que mi UICollectionView no se ha redimensionado correctamente como lo está la barra de navegación, por lo que las celdas que suben donde la barra de navegación UTILIZADA se recortan, ya que están fuera de los límites de la vista de colección. ¿Sabes por qué esto podría estar sucediendo?
Tim Camber
44
Cifras: encontré mi solución segundos después de escribir este comentario. Tenía que asegurarme de que extendedLayoutIncludesOpaqueBarsestaba configurado YESen miUICollectionViewController
Tim Camber
@TimArnold ¡Eso es increíble! Había otro tipo que tenía el mismo problema , y espero que su solución lo ayude.
Mazyod
33

Puedes echar un vistazo a mi GTScrollNavigationBar . He subclasificado UINavigationBar para que se desplace según el desplazamiento de un UIScrollView.

Nota: Si tiene una barra de navegación OPAQUE, la vista de desplazamiento debe EXPANDIRSE a medida que la barra de navegación se OCULTA. Esto es exactamente lo que hace GTScrollNavigationBar. (Al igual que en Safari, por ejemplo, en iOS).

Thuy
fuente
Por cierto para cualquiera que lea, exactamente cómo llamar a initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie
@Thuy gran trabajo hombre! Así que tengo esto funcionando perfectamente en un controlador de vista de tabla, excepto una cosa ... Tengo que actualizar implementado en la parte superior. Se vuelve extraño cuando intenta derribar y actualizar. ¿Podría haber una solución para esto?
aherrick
@Thuy también ... digamos que tenía un controlador de vista donde implementé una vista de tabla en la parte inferior. Quiero conectarme a esa vista de tabla que funciona, sin embargo, tengo otra vista que está sentada encima de la vista de tabla que también quiero desaparecer. ¿Cómo podría funcionar esto?
aherrick
25

iOS8 incluye propiedades para ocultar la barra de navegación de forma gratuita. Hay un video de WWDC que lo demuestra, busque "Ver avances del controlador en iOS 8".

Ejemplo :

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}

}

Otras propiedades:

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 a través de http://natashatherobot.com/navigation-bar-interactions-ios8/

Michael Peterson
fuente
12

Tengo algún tipo de solución rápida y sucia para eso. No he realizado ninguna prueba en profundidad, pero esta es la idea:

Esa propiedad mantendrá todos los elementos en la barra de navegación para mi clase UITableViewController

@property (strong, nonatomic) NSArray *navBarItems;

En la misma clase UITableViewController tengo:

-(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];
}

Eso es solo para ios> = 7, es feo, lo sé, pero es una forma rápida de lograrlo. Cualquier comentario / sugerencia es bienvenida :)

sin par
fuente
12

Esto funciona para iOS 8 y superior y asegura que la barra de estado aún conserva su fondo

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

Y si desea mostrar la barra de navegación cuando toca la barra de estado, puede hacer esto:

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

Aquí está mi implementación: SherginScrollableNavigationBar .

En mi enfoque, estoy usando KVOpara observar UIScrollViewel estado, por lo que no hay necesidad de usar un delegado (y puede usar este delegado para cualquier otra cosa que necesite).

Valentin Shergin
fuente
Para su información, esto no siempre funciona correctamente. También probé esto y funciona siempre y cuando no estés "rebotando" en la vista de desplazamiento. Parece que KVO no se activa cuando está en la parte de rebote. Sin embargo, la llamada de delegado para contentOffset se activa.
Joris Mans
7

Por favor, pruebe esta solución mía y hágame saber por qué esto no es tan bueno como las respuestas 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
fuente
1
Hice algo similar a esto, y esta es fácilmente la mejor solución. He intentado varios hacks y bibliotecas, y este es el único que funciona para iOS 9 con un TableView que no cubre toda la pantalla. ¡Prestigio!
skensell
6

Una forma de lograr esto es la siguiente.

Registre su controlador de vista para que sea el UIScrollViewDelegatesuyo, UITableViewpor ejemplo.

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

Desde dentro de los UIScrollViewDelegatemétodos, puede obtener el nuevo contentOffset y traducirlo UINavigationBarhacia arriba o hacia abajo en consecuencia.

La configuración alfa de las subvistas también se puede hacer en función de algunos valores de umbral y factores que puede establecer y calcular.

¡Espero eso ayude!

Diana Sule
fuente
Se encontró una publicación similar aquí
Diana Sule el
Gracias diana! Sospeché que podría tener que implementar los métodos UIScrollViewDelegate, pero parecía que podría ser un poco exagerado. Una vez que descubra esto, lo publicaré. ¡Salud!
El Mocoso
4

Además de la respuesta de Iwburk, agregué lo siguiente para solucionar el problema alfa en las barras de navegación no personalizadas y restablecer la barra de navegación en el 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];
}
hielo azul
fuente
¿Hay alguna otra manera que recorrer las subvistas?
thisiscrazy4
Por lo que pude ver en una barra de navegación no personalizada, hay 4 subvistas totales: _UINavigationBarBackground, UINavigationItemView, UINavigationItemButtonView y _UINavigationBarBackIndicatorView. El ciclo es bastante rápido y no parece afectar el rendimiento de mi aplicación.
blueice
Dado que _UINavigationBarBackground parece ser siempre la primera subvista, puede acceder al resto directamente: ((UIView *) self.navigationController.navigationBar.subviews [1]). Alpha = alpha;
blueice
4

Estaba buscando una solución que permitiera cualquier estilo y comportamiento. Notarás que el comportamiento de condensación de barras es diferente en muchas aplicaciones diferentes. Y, por supuesto, el aspecto de la barra es totalmente diferente entre las aplicaciones.

Creé una solución para este problema con https://github.com/bryankeller/BLKFlexibleHeightBar/

Puede definir sus propias reglas de comportamiento para controlar cómo y cuándo la barra se encoge y crece, y puede definir exactamente cómo desea que las subvistas de la barra reaccionen a la barra que se condensa o crece.

Eche un vistazo a mi proyecto si desea mucha flexibilidad para crear cualquier tipo de barra de encabezado que pueda imaginar.

blkhp19
fuente
¿Cómo puedo agregar un botón a este CustomHeaderView que se ocultará cuando me desplace hacia arriba? No necesito un botón estático. ¿Es posible? Intenté crear un botón como una subvista. Pero no recibe ningún toque.
abhimuralidharan
3

Estaba tratando de emular este comportamiento en una situación en la que necesitaba un encabezado personalizado sobre un UITableView. Hice rodar mi propia barra de "navegación" porque se encuentra debajo de un montón de otras cosas en la página y quería que los encabezados de sección siguieran el comportamiento predeterminado de "acoplamiento". Creo que encontré una forma bastante inteligente y sucinta de ajustar un UITableView / UIScrollView junto con otro objeto en un estilo similar al que se ve en Facebook / Instagram / Chrome / etc. aplicaciones

En mi archivo .xib, tengo mis componentes cargados en una vista de forma libre: http://imgur.com/0z9yebJ (lo siento, no tengo el representante de las imágenes en línea)

Observe que, en la barra lateral izquierda, la tabla está ordenada detrás de la vista del encabezado principal. No se puede ver en la captura de pantalla, pero también tiene la misma posición y que la vista del encabezado principal. Como se extiende fuera de la vista, la propiedad contentInset en UITableView se establece en 76 (la altura de la vista del encabezado principal).

Para hacer que la vista del encabezado principal se deslice hacia arriba al unísono con UIScrollView, utilizo los métodos scrollViewDidScroll de UIScrollViewDelegate para realizar algunos cálculos y cambiar el contentInset de UIScrollView, así como el marco de la vista del encabezado 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);
    }
}

La primera instrucción if hace la mayor parte del trabajo pesado, pero tuve que incluir las otras dos para manejar situaciones en las que el usuario arrastra con fuerza y ​​los valores iniciales de contentOffset enviados a scrollViewDidScroll están fuera del rango de la primera instrucción if .

En definitiva, esto está funcionando muy bien para mí. Odio cargar mis proyectos con un montón de subclases hinchadas. No puedo decir si esta es la mejor solución en términos de rendimiento (siempre he dudado en poner cualquier código en scrollViewDidScroll desde que se llama todo el tiempo), pero la huella del código es la más pequeña que he visto en cualquier solución para este problema y no implica anidar un UITableView en un UIScrollView (Apple desaconseja esto en la documentación y los eventos táctiles terminan siendo un poco raros en el UITableView). ¡Espero que esto ayude a alguien!

Brian
fuente
2

Intenté implementar GTScrollNavigationBar pero mi aplicación me exigió que modificara las restricciones de diseño automático. Decidí poner un ejemplo de mi implementación en GitHub en caso de que alguien más tenga que hacer esto con el diseño automático. El otro problema que tuve con la mayoría de las otras implementaciones es que las personas no establecen los límites de la vista de desplazamiento para evitar el efecto de desplazamiento de paralaje que crea mientras se desplaza y ajusta el tamaño de la vista de desplazamiento simultáneamente.

Eche un vistazo a JSCollapsingNavBarViewController si necesita hacer esto con el diseño automático. He incluido dos versiones, una solo con la barra de navegación y otra con una subbarra debajo de la barra de navegación que se contrae antes de colapsar la barra de navegación.

jwswart
fuente
1

Lo intenté de esta manera, espero que ayude. simplemente implemente el código en el método delegado y configúrelo en la vista / subvista deseada

-(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
                }
        }
usuario2968901
fuente
1

Una extensión de la respuesta de @Iwburk ... En lugar de cambiar el origen de la barra de navegación, necesitaba expandir / reducir el tamaño de la barra de navegación.

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

Todavía no funciona con el stoppedScrollingmétodo, publicaré una actualización cuando la tenga

jsetting32
fuente
0

Todos estos enfoques parecen demasiado complicados ... Así que, naturalmente, construí el mío:

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
fuente
navigationBar.height? scrollView.height? ¿Utiliza una extensión en la framepropiedad?
Ely
0

Encontré todas las respuestas dadas en Objective-C. Esta es mi respuesta en Swift 3. Este es un código muy genérico y puede usarse directamente. Funciona tanto con UIScrollView como con 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
    }
}

La lógica de configurar alfa a elementos de navegación se copia de la respuesta @ WayneBurkett y se reescribe en Swift 3.

Dev
fuente