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


128

在新的iOS7 Facebook iPhone应用程序中,当用户向上滚动时navigationBar,它会逐渐隐藏起来,使其完全消失。然后,当用户向下滚动时,navigationBar逐渐显示自己。

您将如何自己实施此行为?我知道以下解决方案,但是它立即消失,并且与用户的滚动手势速度完全无关。

[navigationController setNavigationBarHidden: YES animated:YES];

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


2
同样的问题: stackoverflow.com/questions/21929220/… 请注意,要完全匹配Safari的行为非常困难。那里有一些非常非常复杂的规则!
Fattie

1
在我的项目中,我使用了这个项目,并且效果很好。看一下它的文档。
Vinicius

github.com/bryankeller/BLKFlexibleHeightBar将让您做自己想做的事情,甚至更多。它使您可以准确指定条形图在从最大化到最小化过渡的每个阶段的外观。它甚至可以让您指定自己的行为,因此可以像Safari,Facebook或其他某些应用一样运行。
blkhp19

我没有使用uinavigationbar,而是添加了uiview。复制导航栏的视图将基于滚动而展开和收缩。我使用了scrollViewDidScroll委托方法来完成任务。您可能需要检查并执行以下源代码。dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Answers:


162

@peerless提供的解决方案是一个很好的开始,但是它仅在拖动开始时才启动动画,而无需考虑滚动速度。这比您在Facebook应用程序中获得的体验更为混乱。为了匹配Facebook的行为,我们需要:

  • 隐藏/显示导航栏,其速度与拖动速度成比例
  • 如果在部分隐藏栏时滚动停止,则启动动画以完全隐藏栏
  • 随着导航栏缩小,淡化导航栏的项目。

首先,您需要以下属性:

@property (nonatomic) CGFloat previousScrollViewYOffset;

这里是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];
    }
}

您还需要以下辅助方法:

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

对于稍有不同的行为,请使用以下内容替换滚动时重新else放置条的行(中的块scrollViewDidScroll):

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

这将根据最后一个滚动百分比而不是绝对值来定位条形,这会导致变慢。原始行为更像Facebook,但我也喜欢这一行为。

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


这可行。除非您假定条形按钮项具有自定义视图。就我而言,我没有自定义视图。因此,上面没有隐藏条形按钮。我认为@peerless解决方案更适合隐藏和显示导航栏项目
Dhanush

1
你是对的。我可能会在本周末研究更一般的解决方案。定位默认项目(而不是)应该不太困难customView
韦恩

我尚未解决@Dhanush的问题,但确实有更新。
韦恩2014年

1
我使用库存栏按钮解决了该问题,但是此代码还有另一个问题。如果ScrollViewcontentSize小于帧,则滑动动画不起作用。另外,请确保将所有导航项的Alpha重置为1.0 in viewDidDisappear
Legoless 2014年

1
您是否在使用AutoLayout的控制器上检查了此解决方案?就我而言,如果没有AutoLayout,一切都可以正常工作,但是当它打开时,仍然可以看到下面有一些奇怪的白色条纹
Aliaksandr B. 2014年

52

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

您可以尝试使用

self.navigationController.hidesBarsOnSwipe = YES;

为我工作。

如果您要迅速进行编码,则必须使用这种方式(来自https://stackoverflow.com/a/27662702/2283308

navigationController?.hidesBarsOnSwipe = true

在iOS <8.0
Petar 2015年

1
正如pet60t0所说,我很抱歉,仅适用于iOS 8及更高版本。
PedroRomão2015年

4
之后如何带回去?
C0D3

的行为有点奇怪。我没有进行太多探索,但是如果您滚动得更快,它会再次显示。
PedroRomão2015年

@PedroRomão很好的答案。
Gagan_iOS '16

43

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

我尝试使用提供的解决方案后决定自己做,对我来说,它们要么性能不佳,进入和模板代码的障碍很大,要么缺少导航栏下方的扩展视图。要使用此组件,您需要做的是:

self.shyNavBarManager.scrollView = self.scrollView;

哦,它已经在我们自己的应用中经过了战斗测试。


@TimArnold感谢您的反馈!我已经解决了这个问题,只是还没有更新pod> _ <现在就可以做到!.. Pod更新!
Mazyod 2014年

我再试一下!谢谢!
蒂姆·坎伯

我感谢您的帮助。看起来您的提交有所帮助。我仍然遇到一个奇怪的问题,即我的UICollectionView不能像导航栏一样正确地调整大小,因此,将向上移动要使用的导航栏的位置的单元格会被裁剪,因为它们不在集合视图范围之内。您知道为什么会这样吗?
蒂姆·坎伯2014年

4
数字:写此评论后几秒钟就找到了我的解决方案。我必须确保extendedLayoutIncludesOpaqueBars设置为YES我的UICollectionViewController
Tim Camber 2014年

@TimArnold太棒了!还有另一个人遇到相同的问题,希望您的解决方案对他有帮助。
Mazyod 2014年

33

您可以看一下我的GTScrollNavigationBar。我将UINavigationBar子类化,以使其基于UIScrollView的滚动而滚动。

注意:如果您有OPAQUE导航栏,则滚动视图必须隐藏,因为滚动视图必须展开。这正是GTScrollNavigationBar所做的。(就像在iOS上的Safari中一样。)


顺便说一句,任何阅读的人,究竟如何调用initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie 2014年

@太棒了!因此,除了一件事情外,我已经在一个表视图控制器上完美地工作了……我不得不在顶部实现刷新。尝试下拉并刷新时会变得很奇怪。可能有解决方法吗?
aherrick

@Thuy也...可以说我有一个视图控制器,在该控件的底部实现了一个表格视图。我想连接到可以使用的表格视图,但是我也想坐在表格视图上方的另一个视图也要消失。这怎么工作?
aherrick

25

iOS8包含用于免费隐藏导航栏的属性。有一个WWDC视频演示它,搜索“ iOS 8中的View Controller Advances”。

范例

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}

}

其他特性:

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

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


12

我对此有一个快速而肮脏的解决方案。尚未进行任何深入的测试,但这是一个主意:

该属性会将所有项保留在UITableViewController类的导航栏中

@property (strong, nonatomic) NSArray *navBarItems;

在同一个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];
}

这仅适用于ios> = 7,我知道这很丑陋,但这是实现此目的的快速方法。任何意见/建议,欢迎:)


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

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

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

10

这是我的实现:SherginScrollableNavigationBar

在我的方法中,我KVO用于观察UIScrollView的状态,因此没有必要使用委托(您可以将委托用于其他需要的对象)。


仅供参考,这并不总是能正常工作。我也尝试过,只要您不“弹跳” scrollview,它就可以工作。在弹跳部分中似乎未触发KVO。不过,将触发对contentOffset的委托调用。
Joris Mans

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

1
我做了类似的事情,这无疑是最好的解决方案。我已经尝试了几种技巧和库,并且这是唯一适用于iOS 9且tableView不能覆盖整个屏幕的工具。荣誉!
skensell's

6

以下是我完成此操作的一种方法。

将您的视图控制器注册UIScrollViewDelegate为您UITableView的。

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

在de UIScrollViewDelegate方法内,您可以获取新的contentOffset并UINavigationBar相应地上下转换。

也可以根据一些阈值和您可以设置和计算的因素来设置子视图的Alpha。

希望能帮助到你!



谢谢戴安娜!我怀疑我可能必须实现UIScrollViewDelegate方法,但似乎有点矫kill过正。一旦我弄清楚了这个东西,我就会把它发布出来。干杯!
El Mocoso

4

除了Iwburk的答案外,我还添加了以下内容,以解决非自定义导航栏上的alpha问题,并在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];
}

除了循环浏览子视图之外,还有其他方法吗?
thisiscrazy4 2014年

根据我在非自定义导航栏上看到的信息,总共有4个子视图:_UINavigationBarBackground,UINavigationItemView,UINavigationItemButtonView和_UINavigationBarBackIndicatorView。循环非常快,似乎不会影响我的应用程序的任何性能。
blueice

由于_UINavigationBarBackground似乎总是第一个子视图,因此您可以直接访问其余子视图:((UIView *)self.navigationController.navigationBar.subviews [1])。alpha = alpha;
blueice 2014年

4

我一直在寻找一种允许任何样式和行为的解决方案。您会注意到,在许多不同的应用程序中,条的冷凝行为是不同的。当然,应用之间的标杆外观完全不同。

我使用https://github.com/bryankeller/BLKFlexibleHeightBar/创建了此问题的解决方案

您可以定义自己的行为规则来控制钢筋的收缩方式和增长时间,以及可以精确定义所需的子视图对钢筋的缩合或增长做出反应的方式。

如果您想要很大的灵活性来制作可以想到的任何标题栏,请查看我的项目。


我如何在此customHeaderView中添加一个按钮,该按钮在向上滚动时会隐藏。我不需要静态按钮。有可能吗?我尝试将一个按钮创建为子视图。
abhimuralidharan

3

我试图在需要一个围绕UITableView的自定义标头的情况下模拟此行为。我滚动了自己的“导航”栏,因为它位于页面上的其他内容之下,并且我希望节标题遵循默认的“对接”行为。我想我找到了一种非常聪明和简洁的方法来调整UITableView / UIScrollView和另一个对象,其样式类似于在Facebook / Instagram / Chrome / etc中看到的样式。应用。

在我的.xib文件中,我的组件已加载到自由格式视图中:http : //imgur.com/0z9yebJ(对不起,没有内嵌图像的代表)

请注意,在左侧边栏中,该表在主标题视图后面排序。您无法从屏幕截图中分辨出来,但它的y位置与主标题视图相同。由于它不可见,因此UITableView的contentInset属性设置为76(主标题视图的高度)。

为了使主标题视图与UIScrollView一起向上滑动,我使用UIScrollViewDelegate的scrollViewDidScroll方法执行一些计算并更改UIScrollView的contentInset以及主标题视图的框架。

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

第一个if语句完成了大部分繁重的工作,但是我不得不包括其他两个if语句来处理用户用力拖动并且发送给scrollViewDidScroll的初始contentOffset值超出第一个if范围的情况语句。

最终,这对我来说真的很好。我讨厌用一堆肿的子类来加载我的项目。我不能说这是否是性能最佳的解决方案(因为一直以来,我一直很犹豫将任何代码放入scrollViewDidScroll中),但是代码占用空间是我所见过的最小的代码解决此问题的方法,它不涉及在UIScrollView中嵌套UITableView(Apple在文档中建议不要这样做,并且触摸事件在UITableView上有点时髦)。希望这对某人有帮助!



2

我尝试实现GTScrollNavigationBar,但我的应用程序要求我修改自动布局约束。我决定在GitHub上放置我的实现示例,以防其他人必须使用自动布局来实现。我在大多数其他实现中遇到的另一个问题是,人们不会设置滚动视图的边界,以避免在同时滚动和调整滚动视图大小时创建的视差滚动效果。

如果您需要使用自动布局功能,请查看JSCollapsingNavBarViewController。我提供了两个版本,一个仅带有导航栏,另一个带有在导航栏下方的子栏,该子栏在折叠导航栏之前会先折叠。


1

我以这种方式尝试过,希望对您有所帮助。只需在委托方法中实现代码并将其设置为所需的视图/子视图

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

1

@Iwburk答案的扩展...除了更改导航栏的原点,我还需要扩展/缩小导航栏的大小。

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

它尚不适用于该stoppedScrolling方法,当我有更新时请发表更新


0

所有这些方法似乎都过于复杂...自然,我建立了自己的方法:

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
    }

}

navigationBar.height?scrollView.height?您在frame物业上使用扩展名吗?
伊利,

0

我找到了Objective-C中给出的所有答案。这是我在Swift 3中的答案。这是非常通用的代码,可以直接使用。它与UIScrollView和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
    }
}

@alpha设置为导航项的逻辑是从@ WayneBurkett答案复制并在Swift 3中重写的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.