Imiter Facebook Masquer / Afficher l'expansion / Contracter la barre de navigation

128

Dans la nouvelle application iPhone Facebook iOS7, lorsque l'utilisateur fait défiler vers le haut, il navigationBarse cache progressivement jusqu'à un point où il disparaît complètement. Ensuite, lorsque l'utilisateur fait défiler vers le bas, le navigationBars'affiche progressivement.

Comment implémenteriez-vous ce comportement vous-même? Je connais la solution suivante, mais elle disparaît immédiatement et elle n'est pas du tout liée à la vitesse du geste de défilement de l'utilisateur.

[navigationController setNavigationBarHidden: YES animated:YES];

J'espère que ce n'est pas un doublon car je ne suis pas sûr de la meilleure façon de décrire le comportement «expansion / contraction».

El Mocoso
la source
2
Mêmes problèmes: stackoverflow.com/questions/21929220/... Notez qu'il est incroyablement difficile de faire correspondre absolument le comportement de Safari. Il y a des règles très, très compliquées là-dedans!
Fattie
1
Dans mon projet, j'ai utilisé ce projet et cela a très bien fonctionné. Jetez un œil à sa documentation.
Vinicius
github.com/bryankeller/BLKFlexibleHeightBar vous permettra de faire ce que vous voulez et plus encore. Il vous permet de spécifier exactement à quoi ressemble la barre à chaque étape de sa transition de maximisé à minimisé. Il vous permet même de spécifier vos propres comportements, afin qu'il puisse agir comme Safari, Facebook ou une autre application.
blkhp19
Je n'ai pas utilisé d'uinavigationbar mais ai ajouté un uiview à la place. La vue reproduisant la barre de navigation s'agrandit et se contracte en fonction du défilement. J'ai utilisé la méthode de délégué scrollViewDidScroll pour accomplir la tâche. Vous voudrez peut-être vérifier et exécuter le code source ci-dessous. Dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Réponses:

162

La solution donnée par @peerless est un bon début, mais elle ne lance une animation que chaque fois que le glissement commence, sans tenir compte de la vitesse du défilement. Cela se traduit par une expérience plus saccadée que celle que vous obtenez dans l'application Facebook. Pour correspondre au comportement de Facebook, nous devons:

  • masquer / afficher la barre de navigation à une vitesse proportionnelle à la vitesse de la traînée
  • lancer une animation pour masquer complètement la barre si le défilement s'arrête lorsque la barre est partiellement masquée
  • fondu les éléments de la barre de navigation à mesure que la barre se rétrécit.

Tout d'abord, vous aurez besoin de la propriété suivante:

@property (nonatomic) CGFloat previousScrollViewYOffset;

Et voici les UIScrollViewDelegateméthodes:

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

Vous aurez également besoin de ces méthodes d'assistance:

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

Pour un comportement légèrement différent, remplacez la ligne qui repositionne la barre lors du défilement (le elsebloc entrant scrollViewDidScroll) par celle-ci:

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

Cela positionne la barre en fonction du dernier pourcentage de défilement, au lieu d'un montant absolu, ce qui entraîne un fondu plus lent. Le comportement original ressemble plus à Facebook, mais j'aime aussi celui-ci.

Remarque: cette solution est uniquement disponible sur iOS 7+. Assurez-vous d'ajouter les vérifications nécessaires si vous prenez en charge les anciennes versions d'iOS.

Wayne
la source
Cela marche. Sauf que vous supposez que les éléments du bouton de la barre ont des vues personnalisées. Dans mon cas, je n'ai pas de vue personnalisée. Donc, ce qui précède ne cache pas les boutons de la barre. Je pense que la solution @peerless est meilleure pour cacher et afficher les éléments de la barre de navigation
Dhanush
1
Vous avez raison. Je vais peut-être examiner une solution plus générale ce week-end. Ne devrait pas être trop difficile de cibler les éléments par défaut au lieu de customView.
Wayne
Je n'ai pas résolu le problème de @ Dhanush mais j'ai une mise à jour.
Wayne
1
J'ai résolu le problème avec les boutons de la barre de stock, mais ce code a un autre problème. Si ScrollViews contentSizeest plus petit que frame, l'animation du glissement ne fonctionne pas. Assurez-vous également de réinitialiser l'alpha de tous les éléments de navigation à 1,0 po viewDidDisappear.
Legoless
1
Avez-vous vérifié cette solution sur les contrôleurs qui sont utilisés AutoLayout? Dans mon cas, tout fonctionne correctement sans AutoLayout, mais quand il est allumé, il est toujours visible une bande blanche étrange en dessous
Aliaksandr B.
52

EDIT: uniquement pour iOS 8 et supérieur.

Vous pouvez essayer d'utiliser

self.navigationController.hidesBarsOnSwipe = YES;

Travaille pour moi.

Si votre codage en swift, vous devez utiliser cette méthode (à partir de https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true
Pedro Romão
la source
ce n'est pas disponible dans iOS <8.0
Petar
1
Comme pet60t0 l'a dit, et je suis désolé, ne fonctionne que pour iOS 8 et supérieur.
Pedro Romão
4
Comment le ramener après ça?
C0D3
le comportement est un peu étrange. Je n'ai pas beaucoup exploré, mais si vous faites défiler plus vite, cela apparaît à nouveau.
Pedro Romão
@ PedroRomão excellente réponse.
Gagan_iOS
43

Voici une autre implémentation: TLYShyNavBar v1.0.0 est sorti!

J'ai décidé de créer les miennes après avoir essayé les solutions fournies, et pour moi, elles fonctionnaient mal, avaient une barrière élevée d'entrée et un code de plaque de chaudière, ou n'avaient pas de vue d'extension sous la barre de navigation. Pour utiliser ce composant, il vous suffit de:

self.shyNavBarManager.scrollView = self.scrollView;

Oh, et c'est testé au combat dans notre propre application.

Mazyod
la source
@TimArnold Merci pour vos commentaires! J'ai résolu ce problème, je n'ai pas encore mis à jour le pod> _ <le fera maintenant! .. Pod mis à jour!
Mazyod
Je vais lui donner une autre chance! Merci!
Tim Camber
J'apprécie ton aide. On dirait que votre commit a aidé. Je reçois toujours un problème étrange où mon UICollectionView n'est pas correctement redimensionné comme la barre de navigation, et donc les cellules qui montent là où la barre de navigation UTILISÉE doit être sont coupées, car elles sont en dehors des limites de la vue de la collection. Savez-vous pourquoi cela peut arriver?
Tim Camber
4
Chiffres: j'ai trouvé ma solution quelques secondes après avoir écrit ce commentaire. Je devais m'assurer que extendedLayoutIncludesOpaqueBarsc'était réglé YESsur monUICollectionViewController
Tim Camber
@TimArnold C'est génial! Il y avait un autre gars qui avait le même problème et j'espère que votre solution l'aidera.
Mazyod
33

Vous pouvez jeter un œil à mon GTScrollNavigationBar . J'ai sous-classé UINavigationBar pour le faire défiler en fonction du défilement d'un UIScrollView.

Remarque: Si vous avez une barre de navigation OPAQUE, la vue de défilement doit se développer lorsque la barre de navigation devient HIDDEN. C'est exactement ce que fait GTScrollNavigationBar. (Tout comme dans Safari par exemple sur iOS.)

Thuy
la source
BTW pour tous ceux qui lisent, exactement comment appeler initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie
@Thuy grand homme de travail! Donc, cela fonctionne parfaitement sur un contrôleur de vue de table, sauf une chose ... J'ai pull to refresh implémenté en haut. Cela devient bizarre lorsque vous essayez de tirer vers le bas et de vous rafraîchir. Pourrait-il y avoir une solution de contournement pour cela?
aherrick
@Thuy aussi ... disons que j'avais un contrôleur de vue où j'ai implémenté une vue de table en bas. Je veux me connecter à cette vue de table qui fonctionne, mais j'ai une autre vue qui se trouve au-dessus de la vue de table que je veux également disparaître. Comment cela pourrait-il fonctionner?
aherrick
25

iOS8 inclut des propriétés pour que la barre de navigation se cache gratuitement. Il y a une vidéo WWDC qui le démontre, recherchez «Afficher les progrès du contrôleur dans iOS 8».

Exemple :

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}

}

Autres propriétés:

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

Trouvé via http://natashatherobot.com/navigation-bar-interactions-ios8/

Michael Peterson
la source
12

J'ai une sorte de solution rapide et sale pour cela. Je n'ai fait aucun test approfondi mais voici l'idée:

Cette propriété conservera tous les éléments de la barre de navigation pour ma classe UITableViewController

@property (strong, nonatomic) NSArray *navBarItems;

Dans la même classe UITableViewController que j'ai:

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

Ce n'est que pour ios> = 7, c'est moche je sais mais c'est un moyen rapide d'y parvenir. Tous les commentaires / suggestions sont les bienvenus :)

hors pair
la source
12

Cela fonctionne pour iOS 8 et supérieur et garantit que la barre d'état conserve toujours son arrière-plan

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

Et si vous souhaitez afficher la barre de navigation lorsque vous appuyez sur la barre d'état, vous pouvez le faire:

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

Voici mon implémentation: SherginScrollableNavigationBar .

Dans mon approche que j'utilise KVOpour observer UIScrollViewl'état de, il n'est donc pas nécessaire d'utiliser un délégué (et vous pouvez utiliser ce délégué pour tout ce dont vous avez besoin).

Valentin Shergin
la source
Pour info, cela ne fonctionne pas toujours correctement. J'ai essayé cela aussi et cela fonctionne tant que vous ne "rebondissez" pas le scrollview. Il semble que KVO ne soit pas déclenché dans la partie de rebond. L'appel délégué pour contentOffset est cependant déclenché.
Joris Mans
7

Veuillez essayer cette solution et dites-moi pourquoi ce n'est pas aussi bon que les réponses précédentes.

- (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
la source
1
J'ai fait quelque chose de similaire, et c'est de loin la meilleure solution. J'ai essayé plusieurs hacks et bibliothèques, et c'est le seul qui fonctionne pour iOS 9 avec une tableView ne couvrant pas tout l'écran. Gloire!
skensell
6

Une façon dont j'ai accompli ceci est la suivante.

Enregistrez votre contrôleur de vue pour être le UIScrollViewDelegatede votre UITableViewpar exemple.

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

À partir de ces UIScrollViewDelegateméthodes, vous pouvez obtenir le nouveau contentOffset et traduire votre UINavigationBarup ou down en conséquence.

La définition de l'alpha des sous-vues peut également être effectuée en fonction de certaines valeurs de seuil et de certains facteurs que vous pouvez définir et calculer.

J'espère que ça aide!

Diana Sule
la source
Trouvé article similaire ici
Diana Sule
Merci Diana! Je soupçonnais que je devrais peut-être implémenter les méthodes UIScrollViewDelegate mais il semblait que cela pourrait être un peu exagéré. Une fois que j'aurai compris cette chose, je la posterai. À votre santé!
El Mocoso
4

En plus de la réponse d'Iwburk, j'ai ajouté ce qui suit pour résoudre le problème alpha sur les barres de navigation non personnalisées et pour réinitialiser la barre de navigation dans la méthode 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];
}
glace bleue
la source
Existe-t-il un autre moyen que de parcourir les sous-vues?
thisiscrazy4
D'après ce que je pourrais dire sur une barre de navigation non personnalisée, il y a 4 sous-vues au total: _UINavigationBarBackground, UINavigationItemView, UINavigationItemButtonView et _UINavigationBarBackIndicatorView. La boucle est assez rapide et ne semble pas affecter les performances de mon application.
blueice
Puisque _UINavigationBarBackground semble toujours être la première sous-vue, vous pouvez simplement accéder directement au reste: ((UIView *) self.navigationController.navigationBar.subviews [1]). Alpha = alpha;
blueice
4

Je cherchais une solution qui permette n'importe quel style et n'importe quel comportement. Vous remarquerez que le comportement de condensation des barres est différent dans de nombreuses applications différentes. Et bien sûr, l'apparence de la barre est totalement différente entre les applications.

J'ai créé une solution pour ce problème avec https://github.com/bryankeller/BLKFlexibleHeightBar/

Vous pouvez définir vos propres règles de comportement pour contrôler comment et quand la barre rétrécit et grossit, et vous pouvez définir exactement comment vous voulez que les sous-vues de la barre réagissent à la condensation ou à la croissance de la barre.

Jetez un œil à mon projet si vous voulez beaucoup de flexibilité pour créer n'importe quel type de barre d'en-tête que vous pouvez imaginer.

blkhp19
la source
Comment puis-je ajouter un bouton à ce customHeaderView qui se cachera lorsque je défilerai vers le haut.Je n'ai pas besoin de bouton statique.Est-ce possible? J'ai essayé de créer un bouton en tant que sous-vue.Mais il ne reçoit aucune touche.
abhimuralidharan
3

J'essayais d'émuler ce comportement dans une situation où j'avais besoin d'un en-tête personnalisé assis sur un UITableView. J'ai roulé ma propre barre de "navigation" car elle se trouve sous un tas d'autres choses sur la page et je voulais que les en-têtes de section suivent le comportement "d'ancrage" par défaut. Je pense que j'ai trouvé un moyen assez intelligent et succinct d'ajuster un UITableView / UIScrollView avec un autre objet dans un style similaire à celui vu dans Facebook / Instagram / Chrome / etc. applications.

Dans mon fichier .xib, mes composants sont chargés dans une vue de forme libre: http://imgur.com/0z9yebJ (désolé, je n'ai pas le représentant pour insérer des images)

Notez que, dans la barre latérale gauche, le tableau est rangé derrière la vue d'en-tête principale. Vous ne pouvez pas le dire sur la capture d'écran, mais il a également la même position y que la vue d'en-tête principale. Comme il s'étend hors de vue, la propriété contentInset sur UITableView définie sur 76 (la hauteur de la vue d'en-tête principale).

Pour faire glisser la vue d'en-tête principale vers le haut à l'unisson avec UIScrollView, j'utilise les méthodes scrollViewDidScroll de UIScrollViewDelegate pour effectuer des calculs et modifier le contentInset de UIScrollView ainsi que le cadre de la vue d'en-tête principale.

- (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 première instruction if fait l'essentiel du gros du travail, mais j'ai dû inclure les deux autres pour gérer les situations où l'utilisateur fait glisser avec force et les valeurs initiales contentOffset envoyées à scrollViewDidScroll sont en dehors de la plage du premier if instruction .

En fin de compte, cela fonctionne très bien pour moi. Je déteste charger mes projets avec un tas de sous-classes gonflées. Je ne peux pas dire si c'est la meilleure solution en termes de performances (j'ai toujours hésité à mettre du code dans scrollViewDidScroll car il est appelé tout le temps), mais l'empreinte du code est la plus petite que j'ai vue dans aucun solution à ce problème et cela n'implique pas d'imbriquer un UITableView dans un UIScrollView (Apple déconseille cela dans la documentation et les événements tactiles finissent un peu génial sur l'UITableView). J'espère que cela aide quelqu'un!

Brian
la source
3

HidingNavigationBar est un excellent projet qui masque la barre de navigation et la barre d'onglets si vous le souhaitez.

HidingNavigationBar prend en charge le masquage / l'affichage des éléments de vue suivants:

UINavigationBar

UINavigationBar et une extension UIView

UINavigationBar et une UIToolbar

UINavigationBar et un UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar

Michael Peterson
la source
2

J'ai essayé d'implémenter GTScrollNavigationBar mais mon application m'a obligé à modifier les contraintes de mise en page automatique. J'ai décidé de mettre un exemple de mon implémentation sur GitHub au cas où quelqu'un d'autre devrait le faire avec la mise en page automatique. L'autre problème que j'ai eu avec la plupart des autres implémentations est que les gens ne définissent pas les limites de la vue de défilement pour éviter l'effet de défilement de parallaxe que vous créez pendant que vous faites défiler et ajustez la taille de la vue de défilement simultanément.

Consultez JSCollapsingNavBarViewController si vous devez le faire avec la disposition automatique. J'ai inclus deux versions, une avec la barre de navigation uniquement et une autre avec une sous-barre sous la barre de navigation qui s'effondre avant de réduire la barre de navigation.

jwswart
la source
1

Je l'ai essayé de cette façon, j'espère que cela aidera. implémentez simplement le code dans la méthode déléguée et définissez-le sur la vue / sous-vue souhaitée

-(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
la source
1

Une extension de la réponse de @Iwburk ... Au lieu de changer l'origine de la barre de navigation, j'avais besoin d'agrandir / réduire la taille de la barre de navigation.

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

Cela ne fonctionne pas encore avec la stoppedScrollingméthode, je poste une mise à jour quand je l'ai

jsetting32
la source
0

Toutes ces approches semblent trop compliquées ... Alors naturellement, j'ai construit la mienne:

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
la source
navigationBar.height? scrollView.height? Utilisez-vous une extension sur la framepropriété?
Ely
0

J'ai trouvé toutes les réponses données en Objective-C. C'est ma réponse dans Swift 3. C'est un code très générique et peut être utilisé directement. Il fonctionne avec UIScrollView et 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 logique de réglage de l'alpha sur les éléments de navigation est copiée à partir de la réponse @ WayneBurkett et réécrite dans Swift 3.

Dev
la source