模拟京东商城实现导航条隐藏功能

样式需求展示-京东导航条

:.gif
需求说明:

1.导航条隐藏功能
2.界面向上滚动的时候,导航条隐藏
3.界面向下滚动的时候,导航条显示


层次结构分析:

核心思路:导航条必须隐藏,显示的顶部的类似于导航条的控件,是我们自定义的UIView,才能实现效果!

层级结构分析:


1.png

思路①:使用图中 - 原谅色的View - 导航条View - 替代navigationBar

==>问题出现 - 这种整个导航条View隐藏的时候,顶部时间View也隐藏了!不符合要求

2.png

思路②.顶部分成三个模块部分相互独立:

  • 顶部时间工具条自己一个View
  • 导航条自己一个View
  • 按钮VIew自己一个独立的View
  • 内容tableView自己独立一个View就不用说了

隐藏导航条 && 界面移动的原理解释

①.界面上移的时候 - 导航View隐藏:

3.png
  • 原理色的导航条View隐藏
  • 按钮View上移
  • tableView上移
  • tabView高度 ++ (加上导航条View的高度)

①.界面上移的时候 - 隐藏的导航View显示:


4.png
  • 原理色的导航条View显示
  • 按钮View下移
  • tableView下移
  • tabView高度 -- (减去刚刚++的导航条View高度)

问题难点:如果知道 下方的tableView滚动方法(怎么知道是向上滚还是向下滚动)

思路1:tableVIew本质是scrollview,判断scrollview的滚动方向,通过contentOffset

思路2:

使用KVO,监听tableVIew的滚动,监听两个值 - NSKeyValueObservingOptionOld && NSKeyValueObservingOptionNew,通过新旧值的 .y值,判断滚动方向。

这里使用的就是思路2的方法:

a.隐藏系统默认的导航条View,然后自定义和导航条一模一样的UIView上去
[self.navigationController setNavigationBarHidden:YES];

b.tableView添加KVO监听滑动方向
[_tableView addObserver:self forKeyPath:NSStringFromSelector(@selector(contentOffset)) options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

c.通过观察者监听值的变化

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    CGFloat oldOffsetY = [change[NSKeyValueChangeOldKey] CGPointValue].y;
    CGFloat newOffsetY = [change[NSKeyValueChangeNewKey] CGPointValue].y;
    CGFloat deltaY = newOffsetY - oldOffsetY;

    if(deltaY >= 0) { 
       //向上滚动}
    else{
      //向下滚动
}

c.在向上滚动的时候 - 设置导航条隐藏 + View上移

    if(deltaY >= 0) {  //向上滚动

        [UIView animateWithDuration:0.25 animations:^{
            
            //隐藏导航条
            _navigationView.hidden = YES;
            
            //按钮View上移导航条View的高度 - Y值改变
            CGRect tempShowViewFrame =  _showView.frame;
            tempShowViewFrame.origin.y -= navigationBarH;
            _showView.frame = tempShowViewFrame;
            
             //tableView上移导航条View的高度 - Y值改变 && 高度 增加导航条的view的高度
            CGRect tempTableViewFrame = _tableView.frame;
            tempTableViewFrame.origin.y -= navigationBarH;
            tempTableViewFrame.size.height += navigationBarH;
            _tableView.frame = tempTableViewFrame;
            
        }];
        
    }

d.在界面向下滚动的时候 - 设置导航条View显示 + View下移

    else {
        //向下滚动 - show
        [UIView animateWithDuration:0.25 animations:^{
            
            _navigationView.hidden = NO;
            
            CGRect tempShowViewFrame =  _showView.frame;
            tempShowViewFrame.origin.y += navigationBarH;
            _showView.frame = tempShowViewFrame;
            
            CGRect tempTableViewFrame = _tableView.frame;
            tempTableViewFrame.origin.y += navigationBarH;
            tempTableViewFrame.size.height -= navigationBarH;
            _tableView.frame = tempTableViewFrame;
            
        }];
    }

核心代码如上,其实本质就是通过KVO观察tableView的滚动方向,然后设置对应的View显示 && 位置变化

Demo展示:


singleVC.gif



进阶篇:跨控制器改变View的显示


demo中的View都在同一个界面,可以直接在observeValueForKeyPath方法中,直接通过 UIView的成员变量改View的状态,但是如果跨控制器呢?

complexVC.gif

如图:此界面的顶部三个按钮,分别对应响应的三个控制器[‘全部’,‘测试1’,‘测试2’],控制器结构分析:


5.png
  • 导航View && 按钮View && 按钮在外层的控制器上
  • 每个按钮对应各自的单独一个控制器,显示内容
  • 按钮对应的内部VC的view 添加到外层的VC的View上,才能得到显示
  • 最终显示的tableView,其实是按钮VC里面的- [btn->内部VC的view addsubView:tableVIew]
  • 所以简单的说,就是tableVIew和其他的View不在同一个控制器里

思路:跨控制器传值

(这里打算使用 - 代理模式)


==>思路:

  1. 因为是通过tableView滚动方向,判断View的显示隐藏 && 位置,所以判断在tableView对应的控制器上;
  2. 外部控制器根据tableView控制器的滚动方向而做出相应的变化,所以外部控制器要成为代理对象,协议声明写在tableView控制器上
  3. 外部控制要显示还是隐藏,tableView控制器要告诉他,所以代理方法要传值
//tableView控制器的.h文件
@protocol HZOrderNavigationViewDelegate <NSObject>

- (void)changeNavigationViewShow:(BOOL)hidden;
@end


tableView控制器,还是一样通过KVO监听tableView的contentOffSet,然后在observeValueForKeyPath 方法中,由于无法直接控制外部VC的界面属性,所以通过代理传值,告诉外部控制器,界面要发生的变化

//tableView控制器的.m文件 
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    CGFloat oldOffsetY          = [change[NSKeyValueChangeOldKey] CGPointValue].y;
    CGFloat newOffsetY          = [change[NSKeyValueChangeNewKey] CGPointValue].y;
    CGFloat deltaY              = newOffsetY - oldOffsetY;
    if(deltaY >= 0) {  //向上滚动
        //变成执行代理方法,通知外部VC的界面发生改变
        if ([_delegate respondsToSelector:@selector(changeNavigationViewShow:)]) {
            
            [_delegate changeNavigationViewShow:YES];
        }
}
//外部控制器接收到tableView控制器传来的值之后,做出的界面改变
-(void)changeNavigationViewShow:(BOOL)hidden{
    
    if (hidden) {
        //导航条隐藏
        [UIView animateWithDuration:0.25 animations:^{
            
            _navigationView.hidden = YES;
            //按钮View位置改变
            CGRect tempShowViewFrame =  _topBtnsView.frame;
            tempShowViewFrame.origin.y -= navigationBarH;
            _topBtnsView.frame = tempShowViewFrame;
            
            //修改scrollview - 按钮VC对应的view添加到这个scrollview上的
            CGRect tempScrollViewFrame = _contentScrollView.frame;
            tempScrollViewFrame.origin.y -= navigationBarH;
            tempScrollViewFrame.size.height += navigationBarH;
            _contentScrollView.frame = tempScrollViewFrame;
        }];

    }

原理其实和同一个控制器里面改变UIView的属性一样,只是这里跨了控制器,无法拿到属性,所以是通过代理传值告诉外部的那个控制器做出相应的改变而已,本质核心不变。


几个小细节:

  • 细节1:
7.png
8.png

所以界面变化的步骤应该如下:

  1. 导航View隐藏
  2. 按钮View上移
  3. 外层scrollview上移,高度 ++
  4. tableView要和scrollview一样高度++,但是Y不用移动!!

  • 细节2:判断界面的显示or隐藏,如果导航条View已经隐藏了,再怎么上拉,也不能再调用使界面再次隐藏的办法,同时不能再让下方两个View的Y值 --;
bug1.gif

如图所示,要添加判断,如果导航条已经隐藏了,按钮View 和 tableView就不要再一直往上跑了,最多就上移一个View的位置就够了,所以要添加判断;

如果是在同一个控制器中,可以添加 - _navigationView.hidden 属性判断

   if(deltaY >= 0) {  //向上滚动
       NSLog(@"向上滚动 - hidden");
       if (_navigationView.hidden == YES) {
           return;
       }

但是这里是跨控制器的,_navigationView属性是在外部控制器上,而不是在tableView的控制器上,所以拿不到!

  • 根据y值判断?


    6.png
9.png

问题出现:tableView是添加到外部控制的内容ScrollView上的,Y值永远是0!所以不能用y值判断!

10.png

解决办法:Y值虽然是0无法进行判断,但是可以通过tableView的高度进行判断!

  if(deltaY >= 0) {  //向上滚动
        
        if (_tableView.frame.size.height == ScreenH - (topTimeToolH + CarInsOrderTopViewH )) {
           //说明tableView上方没有导航条View  - 导航条已经隐藏了,此时上滚就不用再改变位置了
            return;
        }

同理:向下滚动的时候也要添加判断

    else{
        if (_tableView.frame.size.height == ScreenH - (topTimeToolH + CarInsOrderTopViewH + navigationBarH))   return;
        }

  • 细节3:判断的值最好不要用0,不然稍微一碰tableView,界面就发生变化
    if(deltaY >= 50) {  //向上滚动
}
    else if (deltaY <= -50){
}


进阶 - 下拉刷新导致的Bug

下拉刷新Bug.gif

bug说明:如图,只要一使用下拉刷新,就自动调用 - 导航条View隐藏 并且 外部控制器上移效果

下拉刷新的时候,本质上也是拖动tableView,一样会进tableView的监听方法!


项目需求:下拉刷新的时候,不要和gif显示的一样,导致导航条隐藏并使得界面上移!

解决方案 :


11.png

如图,我们发现,下拉的时候,跑出来的 mj_headerView - 高度54,就等于,直接让tableView的contentOffset.y = 54了!

解决如下:所以要进代理方法,判断y的位移量,一定要大于54!大于54才让进入代理方法,例如取个80,否则每次下拉刷新都会进入代理方法改变界面


    if(deltaY >= 80) {  //向上滚动
        
        if (_tableView.frame.size.height == ScreenH - (topTimeToolH + CarInsOrderTopViewH )) {
            return;
        }

最终效果演示:


解决下拉刷新Bug.gif

demo地址~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,619评论 4 59
  • 记起了曾经 许下的誓言 陪我度过 漫漫的冬季 湿润的眼角 敌不过汝心 是不是风雨 还没到尽头 你总是对我说 让我不...
    木木MY阅读 403评论 2 4
  • 金波一旋若太阴, 银面百媚实可怜。 峻岭逶迤鸟绝飞, 天地辽阔书难传。 1983.2.4夜
    果州闻郡阅读 210评论 0 0
  • 幸福是更多的金钱吗?是拥有更多的权力吗?是成为明星吗?是倾城之貌或曼妙身姿?当然这些可以为幸福加分但绝不是幸福的必...
    沐阳燕阅读 415评论 0 0