模仿Facebook隐藏/显示扩展/收缩导航栏

El *_*oso 128 iphone objective-c uinavigationbar ios ios7

在新的iOS7 Facebook iPhone应用程序中,当用户向上滚动时,navigationBar逐渐隐藏自己到完全消失的程度.然后,当用户向下滚动时,navigationBar逐渐显示自己.

你会如何自己实现这种行为?我知道以下解决方案,但它立即消失,并没有与用户的滚动手势的速度挂钩.

[navigationController setNavigationBarHidden: YES animated:YES];
Run Code Online (Sandbox Code Playgroud)

我希望这不是重复,因为我不确定如何最好地描述"扩展/收缩"行为.

Way*_*ett 162

@peerless给出的解决方案是一个很好的开始,但它只会在拖动开始时启动动画,而不考虑滚动的速度.这会导致比Facebook应用程序更糟糕的体验.为了匹配Facebook的行为,我们需要:

  • 以与拖动速率成比例的速率隐藏/显示导航栏
  • 如果在部分隐藏栏时滚动停止,则启动动画以完全隐藏栏
  • 当条形缩小时淡出导航条的项目.

首先,您需要以下属性:

@property (nonatomic) CGFloat previousScrollViewYOffset;
Run Code Online (Sandbox Code Playgroud)

以下是UIScrollViewDelegate方法:

- (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];
    }
}
Run Code Online (Sandbox Code Playgroud)

您还需要这些辅助方法:

- (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];
    }];
}
Run Code Online (Sandbox Code Playgroud)

对于略有不同的行为,请使用以下内容替换滚动(else块中scrollViewDidScroll)时重新定位条形的线:

frame.origin.y = MIN(20, 
                     MAX(-size, frame.origin.y - 
                               (frame.size.height * (scrollDiff / scrollHeight))));
Run Code Online (Sandbox Code Playgroud)

这会根据最后一个滚动百分比而不是绝对量来定位条形,这会导致较慢的淡入淡出.原来的行为更像Facebook,但我也喜欢这个.

注意:此解决方案仅适用于iOS 7+.如果您支持旧版iOS,请务必添加必要的检查.


Ped*_*mão 52

编辑:仅适用于iOS 8及更高版本.

你可以尝试使用

self.navigationController.hidesBarsOnSwipe = YES;
Run Code Online (Sandbox Code Playgroud)

适合我.

如果您使用swift进行编码,则必须使用这种方式(来自/sf/answers/1936389171/)

navigationController?.hidesBarsOnSwipe = true
Run Code Online (Sandbox Code Playgroud)

  • 那之后怎么把它带回来? (4认同)

Maz*_*yod 43

这是另外一个实现:TLYShyNavBar v1.0.0发布了!

我决定在尝试提供的解决方案之后自己制作,对我而言,他们要么表现不佳,要么具有较高的进入障碍和锅炉板代码,要么缺少导航栏下方的扩展视图.要使用此组件,您所要做的就是:

self.shyNavBarManager.scrollView = self.scrollView;
Run Code Online (Sandbox Code Playgroud)

哦,它在我们自己的应用程序中进行了战斗测试.

  • 数字:在撰写此评论后几秒钟找到我的解决方案.我必须确保``UICollectionViewController上的`extendedLayoutIncludesOpaqueBars`设置为`YES` (4认同)

小智 33

你可以看看我的GTScrollNavigationBar.我已经将UINavigationBar子类化,使其基于UIScrollView的滚动进行滚动.

注意:如果您有一个OPAQUE导航栏,则滚动视图必须EXPAND,因为导航栏会获得HIDDEN.这正是GTScrollNavigationBar所做的.(就像在iOS上的Safari一样.)


Mic*_*son 25

iOS8包含可以免费隐藏导航栏的属性.有一个WWDC视频演示它,搜索"iOS 8中的View Controller Advancements".

示例:

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}
Run Code Online (Sandbox Code Playgroud)

}

其他性能:

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 }
}
Run Code Online (Sandbox Code Playgroud)

通过http://natashatherobot.com/navigation-bar-interactions-ios8/找到


pee*_*ess 12

我有一些快速而肮脏的解决方案.没有进行任何深入测试,但这里的想法是:

该属性将保留导航栏中的所有项目以用于我的UITableViewController类

@property (strong, nonatomic) NSArray *navBarItems;
Run Code Online (Sandbox Code Playgroud)

在同一个UITableViewController类中,我有:

-(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];
}
Run Code Online (Sandbox Code Playgroud)

这只适用于ios> = 7,我知道这很丑,但实现这一目标很快.欢迎任何意见/建议:)


Zho*_*wen 12

这适用于iOS 8及更高版本,并确保状态栏仍保留其背景

self.navigationController.hidesBarsOnSwipe = YES;
CGRect statuBarFrame = [UIApplication sharedApplication].statusBarFrame;
UIView *statusbarBg = [[UIView alloc] initWithFrame:statuBarFrame];
statusbarBg.backgroundColor = [UIColor blackColor];
[self.navigationController.view addSubview:statusbarBg];
Run Code Online (Sandbox Code Playgroud)

如果要在点击状态栏时显示导航栏,可以执行以下操作:

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
     self.navigationController.navigationBarHidden = NO;
}
Run Code Online (Sandbox Code Playgroud)


Val*_*gin 10

这是我的实现:SherginScrollableNavigationBar.

在我的方法中,我KVO用于观察UIScrollView状态,因此没有必要使用委托(并且您可以将此委托用于您需要的任何其他内容).


Nis*_*ant 7

请尝试我的解决方案,让我知道为什么这不如以前的答案好.

- (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];
}
Run Code Online (Sandbox Code Playgroud)


小智 6

我完成此任务的一种方法如下.

注册您的视图控制器UIScrollViewDelegate就是您UITableView的例子.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
Run Code Online (Sandbox Code Playgroud)

在de UIScrollViewDelegate方法中,您可以获得新的contentOffset并UINavigationBar相应地向上或向下翻译.

设置子视图的alpha也可以根据您可以设置和计算的某些阈值和因子来完成.

希望能帮助到你!