Imitate Facebook hide/show expanding/contracting Navigation Bar Imitate Facebook hide/show expanding/contracting Navigation Bar objective-c objective-c

Imitate Facebook hide/show expanding/contracting Navigation Bar


The solution given by @peerless is a great start, but it only kicks off an animation whenever dragging begins, without considering the speed of the scroll. This results in a choppier experience than you get in the Facebook app. To match Facebook's behavior, we need to:

  • hide/show the navbar at a rate that is proportional to the rate of the drag
  • kick off an animation to completely hide the bar if scrolling stops when the bar is partially hidden
  • fade the navbar's items as the bar shrinks.

First, you'll need the following property:

@property (nonatomic) CGFloat previousScrollViewYOffset;

And here are the UIScrollViewDelegate methods:

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

You'll also need these helper methods:

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

For a slightly different behavior, replace the line that re-positions the bar when scrolling (the else block in scrollViewDidScroll) with this one:

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

This positions the bar based on the last scroll percentage, instead of an absolute amount, which results in a slower fade. The original behavior is more Facebook-like, but I like this one, too.

Note: This solution is iOS 7+ only. Be sure to add the necessary checks if you're supporting older versions of iOS.


EDIT: Only for iOS 8 and above.

You can try use

self.navigationController.hidesBarsOnSwipe = YES;

Works for me.

If your coding in swift you have to use this way (from https://stackoverflow.com/a/27662702/2283308)

navigationController?.hidesBarsOnSwipe = true


Here is one more implementation: TLYShyNavBar v1.0.0 released!

I decided to make my own after trying the solutions provided, and to me, they were either performing poorly, had a a high barrier of entry and boiler plate code, or lacked the extension view beneath the navbar. To use this component, all you have to do is:

self.shyNavBarManager.scrollView = self.scrollView;

Oh, and it is battle tested in our own app.