美文网首页iOS-项目、功能ios专题iOS工作环境搭建
iOS 实现多个tableView滑动悬停的一种方法(仿简书个人

iOS 实现多个tableView滑动悬停的一种方法(仿简书个人

作者: 萌小菜 | 来源:发表于2016-09-10 17:57 被阅读3755次
    效果图

    ** 测试环境 : **Xcode8.1 iOS10.1

    现在看到好多应用都有类似上面滑动悬停的效果,我之前也写过类似的,现在新项目又有这种效果,之前写得也有点乱,所以就趁这次重写的机会把写这种效果的思路再重新整理一遍。当然我只是为实现这种效果提供一种比较笨的思路,如果大家有更好更简单的思路可以留言。
    实现思路挺简单,但是写起来挺麻烦的,先看层级图:


    UI层级图.png
    • 设置主内容View(backView)
      • 首先在控制器view上面添加一个scrollowView,它是用来左右滑动以显示不现的内容tableView
      • 再在scrollowView上面添加多个(按自己需求)tableView,从左到右依次布局这些tableView,并设置每个tableView的tableHeadView(此处设置tableHeadView只是用来占位用,没有显示的作用)(注意:所有tableView的headView的高度一定要相同)
    // scrollView
        UIScrollView* scrollView = [[UIScrollView alloc] init];
        [self.view addSubview:scrollView];
        scrollView.backgroundColor = [UIColor whiteColor];
        self.scrollView = scrollView;
        scrollView.pagingEnabled = YES;
        scrollView.showsVerticalScrollIndicator = NO;
        scrollView.showsHorizontalScrollIndicator = NO;
        scrollView.delegate = self;
        scrollView.contentSize = CGSizeMake(kScreenWidth * 3, 0);
        [scrollView makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view);
        }];
        
        UIView* headView = [[UIView alloc] init];
        headView.frame = CGRectMake(0, 0, 0, self.headViewHeight + TitleHeight);
        self.tableViewHeadView = headView;
        headView.backgroundColor = [UIColor greenColor]; 
        
        CFContentTableView* table1 = [[CFContentTableView alloc] init];
        table1.delegate = self;
        self.table1 = table1;
        table1.tableHeaderView = headView;
        [scrollView addSubview:table1];
        [table1 makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(scrollView);
            make.width.equalTo(kScreenWidth);
            make.top.equalTo(self.view);
            make.bottom.equalTo(self.view);
        }];
        
        
        CFContentTableView* table2 = [[CFContentTableView alloc] init];
        table2.delegate = self;
        self.table2 = table2;
        table2.tableHeaderView = headView;
        [scrollView addSubview:table2];
        [table2 makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(scrollView).offset(kScreenWidth);
            make.width.equalTo(table1);
            make.top.bottom.equalTo(table1);
        }];
        
        CFContentTableView* table3 = [[CFContentTableView alloc] init];
        table3.delegate = self;
        self.table3 = table3;
        table3.tableHeaderView = headView;
        [scrollView addSubview:table3];
        [table3 makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(scrollView).offset(kScreenWidth*2);
            make.width.equalTo(table1);
            make.top.bottom.equalTo(table1);
        }];
    
    • 设置头部View(headView)
      • 创建需要悬停的headView(非tableHeadView),headView包含悬停view,并把headView添加到控制器View上面,使其正好覆盖在tableHeadView上。
      • 监听tableView的滑动位置(contentOffset),并根据滑动位置去设置headView的frame,还要设置其它tableView的contentOffset,判断如果tableView滑动将要使悬停View超出目标悬停位置时则固定住悬停view。
    - (void)setupHeadView
    {
        UIView* headBackView = [[UIView alloc] init];
        headBackView.backgroundColor = [UIColor blueColor];
        headBackView.frame = CGRectMake(0, 0, kScreenWidth, self.headViewHeight + TitleHeight);
        [self.view addSubview:headBackView];
        self.headBackView = headBackView;
        
        CFTitleBar* titleBarView = [[CFTitleBar alloc] init];
        [headBackView addSubview:titleBarView];
        self.titleBarView = titleBarView;
        titleBarView.backgroundColor = [UIColor whiteColor];
        [titleBarView makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.equalTo(headBackView);
            make.bottom.equalTo(headBackView.mas_bottom);
            make.height.equalTo(TitleHeight);
        }];
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        if (scrollView == self.scrollView || !scrollView.window)
        {
            return;
        }
        CGFloat offsetY = scrollView.contentOffset.y;
        CGFloat originY = 0;
        CGFloat otherOffsetY = 0;
        if (offsetY <= self.headViewHeight)
        {
            originY = -offsetY;
            if (offsetY < 0)
            {
                otherOffsetY = 0;
            }else{
                otherOffsetY = offsetY;
            }
        }else{
            originY = -self.headViewHeight;
            otherOffsetY = self.headViewHeight;
        }
        self.headBackView.frame = CGRectMake(0, originY, kScreenWidth, self.headViewHeight+TitleHeight);
        
        for ( int i = 0; i<self.titleBarView.titles.count; i++ )
        {
            if (i != self.titleBarView.selectedIndex)
            {
                UITableView* contentView = self.scrollView.subviews[i];
                CGPoint offset = CGPointMake(0, otherOffsetY);
                if ([contentView isKindOfClass:[UITableView class]])
                {
                    if (contentView.contentOffset.y < self.headViewHeight || offset.y < self.headViewHeight)
                    {
                        [contentView setContentOffset:offset animated:NO];
                    }
                }
            }
        }
        
    }
    

    以上就是整体的思路,看起来也不难但是�写起来有些麻烦,如果再加上实际应用中的各种逻辑就会更乱一些,而且对于界面跳转会有很多问题,我也在陆续填坑,如果大家在开发这种效果时遇到了坑欢迎留言一起探讨。

    更新(2016年10月11日)

    昨天看了简书的个人页面,感觉做得更好一些,关键是它的headView部分也可以接收滑动,而我之前做的由于headView挡住了tableView导致无法接受滑动。经过昨天半天的各种尝试目前的实现基本和简书效果一致,headView也可以滑动了。
    具体实现自己看代码吧,虽然实现起来挺简单的,但是寻找这种解决方法还是费了不少时间。

    最后奉上demo地址

    相关文章

      网友评论

      • 夜生物:能在标题下面刷新吗
        萌小菜:@夜生物 应该也可以,不过比这个要复杂
      • lanmoyingsheng:核心原理:用tableheaderView撑起来,然后造一个的view罩在原来tableheaderView位置冒充tableheaderView。这是开发中常用的障眼法:smiley: :smiley:
        萌小菜:正解:grin:
        lanmoyingsheng:不过看你的代码,还是学到一些新的东西:blush:
      • luin4:楼主,看了你的demo发现一个问题,就是当tableview 不能满屏的时候,从其他滑动大于满屏tab切换过来后,滑动会有闪现的bug,请问应该怎么修复呢
        萌小菜:@凹凸曼打小怪兽丶 我也没有找到完美的方法。不完美的解决方法就是判断tableView的contentsize如果不满屏,手动填满屏。。。
      • OC笔记:楼主,你好,我看了你的demo,有个问题,如何让demo中的返回按钮也响应上下滑动的手势呢?
      • OC笔记:楼主,你好。请问如何在hitTest里判断手势类型呢?我现在按照你的思路确实实现了基本的布局,但是我的header上还有collectionView(水平方向的),如何让collectionView也能拖动呢?
      • 酷比Xcoder:请教header上的控件能做到像tableViewHeader那样的,响应轻扫滑动和点击事件吗??
        萌小菜:@酷比Xcoder 试试hitTest方法吧
        酷比Xcoder:@萌小菜 大神有大概的思路吗?
        萌小菜:@酷比Xcoder 应该可以,不过有点麻烦,需要自己实现
      • 没能唱给你的歌曲:最近需要要类似功能,但是 其中有一个需求是collectionView ,博主你的demo用collectionView可以吗?
        豌豆大师:你好 我在CFContentTableView里面实现delegate方法 发现方法都不执行 请问什么原因
        没能唱给你的歌曲:@萌小菜 我试了,发现了一些问题,楼主能提供下解决方法吗:
        问题:我用了collectionView 然后在切换btn点击之后,collectionView的头部显示出来了,但是,我一拉collectionView 又闪的一下消失了
        萌小菜:@没能唱给你的歌曲 应该是可以的 你可以试试
      • OpenLee:demo的pch配置成了全路径,改成相对路径好点.封装的还行对,值得学习
        萌小菜:@那一片阳光 拦截了点击事件,具体看demo
        OpenLee:@那一片阳光 额,评论给作者的是在喜欢下面!你搞错了
        萌小菜:@kaiverSmart 感谢支持 :kissing_heart:
      • LaiYoung_:我之前也写过这种,只调用一个方法告诉偏移量即可,只实现了frame的,autolayout的好蛋疼的说
        萌小菜:@LaiYoung_ 恩,看了下你写得,不错。。可以多个tableView就更好了
        LaiYoung_:@萌小菜 我自己写了一个扩展,只是实现了frame的,autolayout实现起来太麻烦了,就没有实现。 https://github.com/CYBoys/PasteEffectDemo
        萌小菜:@LaiYoung_ 哦哦,思路也是这样吗?autolayout 到所谓,我是用习惯了所以现在什么都用autolaout了 :fearful:
      • 若锦:你好,我最近也在做这个效果,想请问一下,你这个demo如果集成MJRefresh的话,mj_header似乎不起作用呢,你在做项目的时候是如何处理刷新与加载的呢 :smile:
        萌小菜:@若锦 我看了下,显示mj_header的问题好解决,但是集成mjrefresh还是有问题,mjrefresh他是用UIView动画设置tableView.contentInset 实现mj_header的显示与隐藏,而不是设置tableView.contentOffset,所以下拉与回弹的时候悬停View会有卡顿。。现在还没想到好的解决方法,考验你的时候到了 :smile: 有解决方法告诉我下 :blush:
        若锦:@萌小菜 应该是集成成功了,走了mj_header的block,但是没有显示刷新时的菊花加载图,而mj_footer是正常的。最近被这个效果弄得头都大了,总是会有一些小bug,找了好多资料,似乎也没找到完美的解决方案 :joy:
        萌小菜:@若锦 我项目中这个界面没用到刷新,所以没有考虑到这方面。添加后看不到mj_header吗? 有时间我也试试。。 :flushed:

      本文标题:iOS 实现多个tableView滑动悬停的一种方法(仿简书个人

      本文链接:https://www.haomeiwen.com/subject/prgyettx.html