美文网首页iOS 知识点iOS实战iOS 实用
实现外卖选餐时两级tableView联动效果

实现外卖选餐时两级tableView联动效果

作者: 杂雾无尘 | 来源:发表于2016-06-03 13:29 被阅读5002次

    最近实现了下饿了么中选餐时两级tableView联动效果,先上效果图,大家感受一下:


    联动效果

    下面说下具体实现步骤:
    首先分解一下,实现这个需求主要是两点,一是点击左边tableView,同时滚动右边tableView到具体的位置。二是拖动右边tableView选中左边tableView对应的某一行。要实现这个需求有一点很重要:左边的tableView每一行对应的是右边tableView的每个分区,OK,Just Do It.

    实现点击左边tableView同时滚动右边tableView,很简单,只需要实现tableView的代理方法- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;然后在代理方法里边拿到右边的tableView,实现让其滚动到第indexPath.row分区,第0行即可,代码如下:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        // 如果点击的是右边的tableView,不做任何处理
        if (tableView == self.rightTableView) return;
        // 点击左边的tableView,设置选中右边的tableView某一行。左边的tableView的每一行对应右边tableView的每个分区
        [self.rightTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] animated:YES scrollPosition:UITableViewScrollPositionTop];
    }
    

    我们这里不处理右边tableView的点击事件,所以if (tableView == self.rightTableView) return;

    接下来我们实现 拖动右边tableView选中左边tableView对应的某一行,我们要动态选中左边的tableView,就需要拿到现在滚动到了那个分区,UITableView有两个代理方法,- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section // 一个头标题即将显示的时候掉用- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section // 一个头标题即将消失的时候掉用
    利用这两个方法就可以拿到当前所在分区实现这个功能了。

    但是我总觉得这个方法不好,还有个更简单的方法,其实tableView有个极不常用,但很牛X的方法,叫做indexPathsForVisibleRows,官方文档解释是:

    The value of this property is an array of NSIndexPath objects each representing a row index and section index that together identify a visible row in the table view. If no rows are visible, the value is nil.

    简单意思就是,它返回一个装着目前屏幕上可见的cell的indexPath集合。

    好的,重点来了,拿到这个集合,不就能拿到目前屏幕上顶端的cell的indexpath了吗,那就如愿以偿的拿到现在所在第indexpath.section个分区了。

    说了这么多,上代码:

     #pragma mark - UIScrollViewDelegate
    -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ // 监听tableView滑动
        // 如果现在滑动的是左边的tableView,不做任何处理
        if ((UITableView *)scrollView == self.leftTableView) return;
        // 滚动右边tableView,设置选中左边的tableView某一行。indexPathsForVisibleRows属性返回屏幕上可见的cell的indexPath数组,利用这个属性就可以找到目前所在的分区
        [self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:self.rightTableView.indexPathsForVisibleRows.firstObject.section inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
    }
    

    稍微解释一下,首先监听scrollView的拖动,本demo不处理左边tableView的滚动,所以if ((UITableView *)scrollView == self.leftTableView) return;
    self.rightTableView.indexPathsForVisibleRows.firstObject.section这句是拿到当前屏幕上可见cell的第一行cell所在的分区,然后让左边的tableView选中第0分区(它只有一个分区)的这一行就OK了。

    欢迎收藏本文章,最后附上demo ,Github传送门:demo

    --------------这叫分割线---------------
    补充下:下边评论提到说点击左边tableView的时候会有阴影效果,其实是这样的,点击左边的tableView,右边的tableView是从当前位置动画滚动到相应位置的,既然有滚动,就会调- (void)scrollViewDidScroll:(UIScrollView *)scrollView这个代理方法,说白了就是拖动了右边tableView,拖动右边的过程中会陆续选中左边。那我想大家就明白了。

    如果不想要这个效果,有两个办法,一个是直接吧- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath中的动画滚动的属性animated值改成NO

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        // 如果点击的是右边的tableView,不做任何处理
        if (tableView == self.rightTableView) return;
        // 点击左边的tableView,设置选中右边的tableView某一行。左边的tableView的每一行对应右边tableView的每个分区
        [self.rightTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] animated:NO scrollPosition:UITableViewScrollPositionTop];
    }
    

    这样做右边的tableView就是无动画滚动了,也就不会再调scrollViewDidScroll:方法。但是如果还想右边tableViewyou滚动效果,另一种解决方法是:把- (void)scrollViewDidScroll:(UIScrollView *)scrollView方法换成- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView这个代理方法方法就行了。饿了么有的界面好像就是这样做的,但是有bug(估计饿了么没测出来),这个方法的注释为

    // called when scroll view grinds to a halt 当滚动视图戛然而止
    --有道翻译如是说

    根据本人亲测,拖拽之后,这个方法调用与否在于你的手指是否在动画停止之前离开了屏幕,如果在动画结束之前手指离开屏幕,此方法调用没什么问题。but,如果动画已经停止,再把手指拿开,这个方法是不会调的。有图有真相:

    WZBLinkageTableViewGif.gif

    解决这个bug的关键在于,让手指离开的时候手动调一次这个代理方法,那怎么才能知道手指什么时候离开呢?scrollView给我们了另一个代理方法:- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset这个方法在结束拖拽的时候调,正好解决了我们的问题:

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
        // 推拽将要结束的时候手动调一下这个方法
        [self scrollViewDidEndDecelerating:scrollView];
    }
    

    OK,解决问题!
    后续,github也会跟进的,感谢大家的喜欢和支持!

    Github已经更新,最新demo在这 demo

    您还可以加入我们的群,大家庭期待您的加入!

    我们的社区我们的社区

    相关文章

      网友评论

      • 899674af0d93:代码在那呢
      • 44bda64c4fc7:可以计算cell的高度吗?
        899674af0d93:代码在那呢
      • liusong007:照着抄一遍,再自己敲一遍:joy:
        899674af0d93:代码在哪呢
      • 嘴爷:我给个建议啊,你用willdisplay方法应该会好点,监听scrollView滚动的方法执行的太多了
      • 35ba870ffa36:当右边滚动到最底部的时候,如果左边没有选中到最低下的cell时,再次点击左侧最底部cell,然后右面滚动时,左侧不联动了。在点击左侧cell时,判断右侧是否是滚动到最底部了,如果时,重置一下当前选择indexpath。
      • 杭城小刘:滚动右边,左边的有缺陷
      • PGOne爱吃饺子:楼主 我把你的currentSelectIndexPath 这个属性给注释掉之后好像也没有发现bug啊,希望楼主指教一下,谢谢
        TryToFlyHigher:这个是避免选了左侧某一个cell,再要选择左侧下一个cell的时候上一个cell还有颜色的作用吧...
      • 1eec26d198fc:强势围观
      • ca4debe814a1:不错不错
      • 星辰大海_王:赞赞赞 :+1: ,if (self.currentSelectIndexPath) self.currentSelectIndexPath = nil;这句放到scrollViewDidScroll:方法里面是不是比较好,否则,右侧上下滚动时,左侧选中那个方法有可能被return,而不会更新。比如左侧选择9或10时,右侧滚动 ,左侧有时并不会更改选中
      • 瑯琊:你好,请问可以实现类似于饿了吗购物车那样的联动吗?就是还有一个scrollview在后面,先是scrollview滑动,等滑动到顶部的时候,tableview才开始滑动。最近碰到这样一个问题,求帮助 :pray:
      • 苜蓿鬼仙:有BUG的,当左侧设为10个,右侧设为十组,每组三个,当刚进来时点击左侧最后一个,就会出现bug
        TryToFlyHigher:@杂雾无尘 @苜蓿鬼仙 TO:杂雾无尘 如果按照@苜蓿鬼仙说的行数和分区数 在数据都统一一致的前提下是不会出现bug的 项目中相关数组的元素的个数和cell行数、分区数都是有联系的 不是吗?:disappointed_relieved:
        杂雾无尘:@苜蓿鬼仙 非常感谢🙏您提的bug,已改正 :grin: ,请到我的github下载最新版本https://github.com/WZBbiao/WZBLinkageTableView
      • 麦穗0615:挺厉害的,我自己敲了一遍
        杂雾无尘:@_正阳_ :+1::+1::+1:
      • 会跳舞的狮子:jiayou ba
        杂雾无尘:@会跳舞的狮子 嗯嗯,多多指教
      • 哈哈我来了:运行不了呢
        杂雾无尘:@哈哈我来了 xcode版本问题吧
      • 雷鸣1010:点击左边的cell为什么会有滚动的阴影呀
        雷鸣1010:@杂雾无尘 谢谢
        杂雾无尘:@雷鸣1010 不想有滚动的阴影解决办法有两个,一个简单粗暴,直接把tableview:didSelectRowAtIndexPath代理方法里把动画滚动的animated值改成NO。另一个方法是把scrollViewDidScroll代理方法换成scrollViewDidEndDecelerating动画结束的时候的这个代理方法就行了。
        杂雾无尘:@雷鸣1010 因为点击左边的,右边是从当前位置动画滚动到相应位置的,既然有滚动,就会调didScroll的代理方法,其实就相当于拖动了右边,你看下这个方法里边做了哪些事情就明白了
      • Lol刀妹:可以,很强势。对代理方法理解比较到位

      本文标题:实现外卖选餐时两级tableView联动效果

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