129

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

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

[navigationController setNavigationBarHidden: YES animated:YES];

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

4

20 回答 20

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

对于稍微不同的行为,将滚动时重新定位栏的行(中的elsescrollViewDidScroll)替换为以下行:

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

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

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

于 2014-01-10T16:54:46.693 回答
52

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

你可以尝试使用

self.navigationController.hidesBarsOnSwipe = YES;

为我工作。

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

navigationController?.hidesBarsOnSwipe = true
于 2015-02-24T15:59:55.750 回答
43

这里还有一个实现:TLYShyNavBar v1.0.0 发布!

在尝试了所提供的解决方案后,我决定自己制作,对我来说,它们要么表现不佳,要么具有很高的进入门槛和样板代码,要么在导航栏下方缺少扩展视图。要使用此组件,您所要做的就是:

self.shyNavBarManager.scrollView = self.scrollView;

哦,它在我们自己的应用程序中经过实战测试。

于 2014-06-27T14:28:07.737 回答
33

你可以看看我的GTScrollNavigationBar。我对 UINavigationBar 进行了子类化,以使其基于 UIScrollView 的滚动进行滚动。

注意:如果您有一个不透明的导航栏,滚动视图必须扩展,因为导航栏隐藏。这正是 GTScrollNavigationBar 所做的。(就像 iOS 上的 Safari 一样。)

于 2013-12-21T15:11:46.307 回答
25

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

示例

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/找到

于 2014-12-26T22:40:43.493 回答
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,我知道这很难看,但可以快速实现这一目标。欢迎任何意见/建议:)

于 2013-12-11T09:26:20.143 回答
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;
}
于 2016-01-30T15:52:26.100 回答
10

这是我的实现:SherginScrollableNavigationBar

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

于 2014-04-02T17:21:17.657 回答
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];
}
于 2015-07-16T19:23:12.217 回答
6

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

UIScrollViewDelegate例如,将您的视图控制器注册为您的视图控制器UITableView

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

从 deUIScrollViewDelegate方法中,您可以获得新的 contentOffset 并UINavigationBar相应地向上或向下翻译。

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

希望能帮助到你!

于 2013-11-07T02:51:20.227 回答
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];
}
于 2014-03-21T15:41:40.230 回答
4

我一直在寻找一种允许任何风格和任何行为的解决方案。您会注意到,条形压缩行为在许多不同的应用程序中是不同的。当然,栏的外观在应用程序之间是完全不同的。

我使用https://github.com/bryankeller/BLKFlexibleHeightBar/为这个问题创建了一个解决方案

您可以定义自己的行为规则来控制条形图收缩和增长的方式和时间,并且您可以准确定义希望条形图的子视图如何对条形图的收缩或增长做出反应。

如果您想灵活地制作您能想到的任何类型的标题栏,请查看我的项目。

于 2015-03-11T00:03:11.130 回答
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语句完成了大部分繁重的工作,但我必须包括另外两个来处理用户强行拖动并且发送到 scrollViewDidScroll 的初始 contentOffset 值超出第一个if语句的范围的情况。

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

于 2014-08-28T21:27:11.937 回答
3

HidingNavigationBar a great project that hides the Navigation Bar and the Tab Bar if you want.

HidingNavigationBar supports hiding/showing of the following view elements:

UINavigationBar

UINavigationBar and an extension UIView

UINavigationBar and a UIToolbar

UINavigationBar and a UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar

于 2016-03-19T06:18:58.853 回答
2

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

如果您需要使用自动布局执行此操作,请查看JSCollapsingNavBarViewController 。我已经包含了两个版本,一个只有导航栏,另一个在导航栏下方有一个子栏,它在折叠导航栏之前折叠。

于 2014-07-07T13:22:40.577 回答
2

适用于 Swift 4,5 - iOS 11 及更高版本

private var previousScrollViewYOffset: CGFloat = 0
private var firstLoad = true
// to avoid scrollViewDidScroll called when first time view controller load
override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        firstLoad = false
    }
// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
    func stoppedScrolling() {
        let frame = self.navigationController?.navigationBar.frame ?? .zero
        if frame.origin.y < UIView.statusBarFrame.size.height {
            self.animateNavBar(to: -frame.size.height + UIView.statusBarFrame.size.height)
        }
    }
    func updateBarButtonItems(alpha: CGFloat) {
        self.navigationItem.leftBarButtonItems?.forEach{ item in
            item.customView?.alpha = alpha
        }
        self.navigationItem.rightBarButtonItems?.forEach{ item in
            item.customView?.alpha = alpha
        }
        self.navigationItem.titleView?.alpha = alpha
        self.navigationController?.navigationBar.tintColor = self.navigationController?.navigationBar.tintColor.withAlphaComponent(alpha)
    }
    
    func animateNavBar(to y: CGFloat) {
        UIView.animate(withDuration: 0.2) {[weak self] in
            var frame: CGRect = self?.navigationController?.navigationBar.frame ?? .zero
            let alpha: CGFloat = frame.origin.y >= y ? 0 : 1
            frame.origin.y = y
            self?.navigationController?.navigationBar.frame = frame
            self?.updateBarButtonItems(alpha: alpha)
        }
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if firstLoad { return }
        var frame = self.navigationController?.navigationBar.frame ?? .zero
        let size = frame.size.height - UIView.statusBarFrame.size.height
        let framePercentageHidden = (UIView.statusBarFrame.size.height - frame.origin.y) / (frame.size.height - 1)
        let scrollOffset = scrollView.contentOffset.y
        let scrollDiff = scrollOffset - previousScrollViewYOffset
        let scrollHeight = scrollView.frame.size.height
        let scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom
        if scrollOffset <= -scrollView.contentInset.top {
            frame.origin.y = UIView.statusBarFrame.size.height
        } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
            frame.origin.y = -size
        } else {
            frame.origin.y = min(UIView.statusBarFrame.size.height, max(-size, frame.origin.y - scrollDiff))
        }
        self.navigationController?.navigationBar.frame = frame
        self.updateBarButtonItems(alpha: 1 - framePercentageHidden)
        self.previousScrollViewYOffset = scrollOffset
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        self.stoppedScrolling()
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if(!decelerate) {
            self.stoppedScrolling()
        }
    }
}

UIView 扩展

extension UIView {
    public static var statusBarFrame: CGRect {
        get {
            return UIApplication.shared.statusBarFrame
        }
    }
}

您应该自定义navigationItem.titleView应用集alpha

于 2020-08-09T07:32:01.167 回答
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
                }
        }
于 2015-05-11T10:06:23.847 回答
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方法,当我有它时会发布更新

于 2015-11-03T00:13:34.013 回答
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
    }

}
于 2015-05-19T17:36:04.640 回答
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 中重写。

于 2017-05-31T11:40:28.917 回答