美文网首页iOS技术资料常用的第三方iOS相关技术
【Objective-C 联动】:两个 TableView 之间

【Objective-C 联动】:两个 TableView 之间

作者: LeeJay | 来源:发表于2016-08-23 15:56 被阅读5665次

前言

现在市面上有很多 app 都有联动功能,有的是两个 TableView 之间的联动,比如美团外卖,百度外卖,饿了么等等。有的是 TableView 与 CollectionView 之间的联动,比如礼物说等等。

TablView 与 CollectionView 之间的联动效果图

本文仿造了美团外卖和礼物说,分别实现了两个 TableView 之间和 TablView 与 CollectionView 之间的联动效果,效果图看下面的 gif 图。先附上 gif 图的 demo 下载链接,【GitHub - OC 版】【GitHub - Swift 版】,配合 demo 一起看文章,效果会更佳。

联动.gif

正文

一、TableView 与 TableView 之间的联动

下面来说下实现两个 TableView 之间联动的主要思路:
先解析数据装入模型,objectWithDictionary:是将字典转化为模型,这个工具是我用 runtime 写的,一行代码解析数据,具体使用方法可以参考我简书上另一篇文章【Objective-C中的Runtime】

NSString *path = [[NSBundle mainBundle] pathForResource:@"meituan" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSArray *foods = dict[@"data"][@"food_spu_tags"];

for (NSDictionary *dict in foods)
{
    CategoryModel *model = [CategoryModel objectWithDictionary:dict];
    [self.categoryData addObject:model];
    
    NSMutableArray *datas = [NSMutableArray array];
    for (FoodModel *f_model in model.spus)
    {
        [datas addObject:f_model];
    }
    [self.foodData addObject:datas];
}

定义两个 TableView:LeftTableView 和 RightTableView。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (_leftTableView == tableView)
    {
        LeftTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier_Left forIndexPath:indexPath];
        FoodModel *model = self.categoryData[indexPath.row];
        cell.name.text = model.name;
        return cell;
    }
    else
    {
        RightTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier_Right forIndexPath:indexPath];
        FoodModel *model = self.productData[indexPath.section][indexPath.row];
        cell.model = model;
        return cell;
    }
}

先将左边的 TableView 关联右边的 TableView:点击左边的 TableViewCell,右边的 TableView 跳到相应的分区列表头部。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    if (_leftTableView == tableView)
    {
        _selectIndex = indexPath.row;
        [_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:_selectIndex] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
}

再将右边的 TableView 关联左边的 TableView:标记一下 RightTableView 的滚动方向,然后分别在 TableView 分区标题即将展示和展示结束的代理函数里面处理逻辑。

  • 1.在 TableView 分区标题即将展示里面,判断当前的 tableView 是 RightTableView, RightTableView 滑动的方向向上,RightTableView 是用户拖拽而产生滚动的(主要判断 RightTableView 是用户拖拽的,还是点击 LeftTableView 滚动的),如果三者都成立,那么 LeftTableView 的选中行就是 RightTableView 的当前 section。
  • 2.在 TableView 分区标题展示结束里面,判断当前的 tableView 是 RightTableView,滑动的方向向下,RightTableView 是用户拖拽而产生滚动的,如果三者都成立,那么 LeftTableView 的选中行就是 RightTableView 的当前 section-1。
// 标记一下 RightTableView 的滚动方向,是向上还是向下
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    static CGFloat lastOffsetY = 0;

    UITableView *tableView = (UITableView *) scrollView;
    if (_rightTableView == tableView)
    {
        _isScrollDown = lastOffsetY < scrollView.contentOffset.y;
        lastOffsetY = scrollView.contentOffset.y;
    }
}

// TableView 分区标题即将展示
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section
{
    // 当前的 tableView 是 RightTableView,RightTableView 滚动的方向向上, RightTableView 是用户拖拽而产生滚动的((主要判断 RightTableView 用户拖拽而滚动的,还是点击 LeftTableView 而滚动的)
    if ((_rightTableView == tableView) && !_isScrollDown && _rightTableView.dragging)
    {
        [self selectRowAtIndexPath:section];
    }
}

// TableView 分区标题展示结束
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
{
    // 当前的 tableView 是 RightTableView,RightTableView 滚动的方向向下, RightTableView 是用户拖拽而产生滚动的(主要判断 RightTableView 用户拖拽而滚动的,还是点击 LeftTableView 而滚动的)
    if ((_rightTableView == tableView) && _isScrollDown && _rightTableView.dragging)
    {
        [self selectRowAtIndexPath:section + 1];
    }
}

// 当拖动右边 TableView 的时候,处理左边 TableView
- (void)selectRowAtIndexPath:(NSInteger)index
{
    [_leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
}

这样就实现了两个 TableView 之间的联动,是不是很简单。

二、TableView 与 CollectionView 之间的联动

TableView 与 CollectionView 之间的联动与两个 TableView 之间的联动逻辑类似。
下面说下实现 TableView 与 CollectionView 之间的联动的主要思路:
还是一样,先解析数据装入模型。

NSString *path = [[NSBundle mainBundle] pathForResource:@"liwushuo" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSArray *categories = dict[@"data"][@"categories"];

for (NSDictionary *dict in categories)
{
    CollectionCategoryModel *model = [CollectionCategoryModel objectWithDictionary:dict];
    [self.dataSource addObject:model];
    
    NSMutableArray *datas = [NSMutableArray array];
    for (SubCategoryModel *sModel in model.subcategories)
    {
        [datas addObject:sModel];
    }
    [self.collectionDatas addObject:datas];
}

定义一个 TableView,一个 CollectionView。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    LeftTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier_Left forIndexPath:indexPath];
    CollectionCategoryModel *model = self.dataSource[indexPath.row];
    cell.name.text = model.name;
    return cell;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier_CollectionView forIndexPath:indexPath];
    SubCategoryModel *model = self.collectionDatas[indexPath.section][indexPath.row];
    cell.model = model;
    return cell;
}

先将 TableView 关联 CollectionView,点击 TableViewCell,右边的 CollectionView 跳到相应的分区列表头部。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    _selectIndex = indexPath.row;
    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:_selectIndex] atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
}

再将 CollectionView 关联 TableView,标记一下 RightTableView 的滚动方向,然后分别在 CollectionView 分区标题即将展示和展示结束的代理函数里面处理逻辑。

  • 1.在 CollectionView 分区标题即将展示里面,判断 当前 CollectionView 滚动的方向向上, CollectionView 是用户拖拽而产生滚动的(主要是判断 CollectionView 是用户拖拽而滚动的,还是点击 TableView 而滚动的),如果二者都成立,那么 TableView 的选中行就是 CollectionView 的当前 section。
  • 2.在 CollectionView 分区标题展示结束里面,判断当前 CollectionView 滚动的方向向下, CollectionView 是用户拖拽而产生滚动的,如果二者都成立,那么 TableView 的选中行就是 CollectionView 的当前 section-1。
// 标记一下 CollectionView 的滚动方向,是向上还是向下
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    static float lastOffsetY = 0;
    
    if (self.collectionView == scrollView)
    {
        _isScrollDown = lastOffsetY < scrollView.contentOffset.y;
        lastOffsetY = scrollView.contentOffset.y;
    }
}

// CollectionView 分区标题即将展示
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
    // 当前 CollectionView 滚动的方向向上,CollectionView 是用户拖拽而产生滚动的(主要是判断 CollectionView 是用户拖拽而滚动的,还是点击 TableView 而滚动的)
    if (!_isScrollDown && collectionView.dragging)
    {
        [self selectRowAtIndexPath:indexPath.section];
    }
}

// CollectionView 分区标题展示结束
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(nonnull UICollectionReusableView *)view forElementOfKind:(nonnull NSString *)elementKind atIndexPath:(nonnull NSIndexPath *)indexPath
{
    // 当前 CollectionView 滚动的方向向下,CollectionView 是用户拖拽而产生滚动的(主要是判断 CollectionView 是用户拖拽而滚动的,还是点击 TableView 而滚动的)
    if (_isScrollDown && collectionView.dragging)
    {
        [self selectRowAtIndexPath:indexPath.section + 1];
    }
}

// 当拖动 CollectionView 的时候,处理 TableView
- (void)selectRowAtIndexPath:(NSInteger)index
{
    [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}

TableView 与 CollectionView 之间的联动就这么实现了,是不是也很简单。

最后

由于笔者水平有限,文中如果有错误的地方,或者有更好的方法,还望大神指出。
附上本文的所有 demo 下载链接,【GitHub - OC 版】【GitHub - Swift 版】,配合 demo 一起看文章,效果会更佳。
如果你看完后觉得对你有所帮助,还望在 GitHub 上点个 star。赠人玫瑰,手有余香。

相关文章

网友评论

  • 厚脸皮小强打不死的小强:大哥,怎么添加头部,跟随tableView一起滑动的效果
  • First灬DKS:博主你好:如果右边的每个section的数据比较少时,比如只有两行,此时会有些问题,请问怎么优化呢?
    First灬DKS:右边滑动时,左边的tableView会出现左后两行无法选中的情况;
    LeeJay:@First灬DKS 你好,请问下,有什么问题呢
  • 西贝人立口:我想问个关于tableview和collection联动的问题,右侧collection的头,如果在上面增加一个按钮判断点击的是哪一个header呢?
    LeeJay:这个用block或者代理
  • 西贝人立口:怎么知道点击的是哪一个collection里面的item呢?
  • 鬼丶白:可以加头部吗
    鬼丶白:@LeeJay 什么时间可以加一个头部的功能吗 向上滑头部跟着滑动至隐藏
    鬼丶白:@LeeJay 还望实现一下我这边需要用一下不过自己实现的不是很好
    LeeJay:可以啊
  • 晓锌:点个赞
  • 剑客十八:感谢分享,赞一个
  • 翀鹰精灵:大神你好啊 之前我也写过这样的功能,但是很水,看了你写的,非常详细 但是还是有一些问题不太明白,想请教一哈,能加个QQ嘛 2807338860
  • feng_dev:大神,我的需求是,左边是一列个数写死的 按钮,右边是一个 table, 然而,这个table 的cell 基本上有好几种,有的有header,有的没有,有的有整个 table 的 header ,有的有 每个 section 的 header,搞得我好乱,每次点击左边按钮,右边都要刷新,返回 header 的那个方法,好像写上就有了,怎么在点击某个按钮的时候去掉。。。。
    LeeJay:@伤感的小孩 没大懂你说的意思,有什么可以帮到你的吗:smiley:
    LeeJay:@伤感的小孩 不知道还怎么帮你
  • feng_dev:发现一只大神。。
  • 天地不仁以万物为刍狗:TableView与TableView之间的联动: 发现一个小BUG!!!
    当_leftTableView的最后一个cell处于选中状态的时候,继续向下滑动_rightTableView会闪退:[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: row (3) beyond bounds (3) for section (0)

    - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
    {
    // 当前的tableView是RightTableView,RightTableView滚动的方向向下,RightTableView是用户拖拽而产生滚动的((主要判断RightTableView用户拖拽而滚动的,还是点击LeftTableView而滚动的)
    if (section == 2) { //当_leftTableView的最后一个cell处于选中状态的时候 return就可以
    return;
    }
    有草木青青:目测你的scrollView的代理用错了
    LeeJay:@天地不仁以万物为刍狗 抱歉,我想问下,你运行的 Xcode 版本和模拟器是多少,因为我这边用Xcode 8 和模拟器iPhone 6,测试的时候,没有重现你说的Bug。
    LeeJay:@天地不仁以万物为刍狗 好的,周末有点事,下周一我来看看。:+1:
  • 笨鸟后飞了:不错,正好用到
    笨鸟后飞了:@LeeJay 这个可以有
    LeeJay:@海东 如果喜欢的话,GitHub上欢迎Star。 :smile:
  • 一个什么都不懂的程序猿:挺好
    万少:右边的collectionView 还有几个分区 这个怎么实现啊 :pray:
    LeeJay:@一个什么都不懂的人 :blush:
  • 3f80ce98ca23:我问下,联动标题的字体大小和颜色什么的能改吗?
    LeeJay:@waitingR 可以
  • 超_iOS:star 了,留着看
    LeeJay:@不知有汉_超 :clap:
  • SkySongK:Xcode8以前居然打不开。。。。。
    LeeJay:@LeeJay 之前用 Xcode 8 版本打开 StroyBoard,导致用 Xcode 8 版本之前的版本编译报错,现已修复,可以去 GitHub 下载最新的代码,有问题或者建议可以联系我 :smile:
    LeeJay:@蓝冰Song 我去看看。
  • xiaomayi2012:大神有个建议,就是当用户点击了左侧tableView中靠下的cell时,选中的cell会自动向上滚动,这样就不用用手指向上滑动了。。提高用户体验。
    xiaomayi2012:@LeeJay 作者你好,你的右侧collectionVIew头部悬停效果如何做的。。
    LeeJay:@xiaomayi2012 谢谢你的建议,github已经更新,可以去下载试试。 :smile:
    LeeJay:@xiaomayi2012 嗯,好,之后我会更新一下。
  • xiaomayi2012:大神正需要这个 非常的感谢啊。。必须star
    LeeJay:@xiaomayi2012 :+1:
  • 00bcb539f3e2:新人刚学iOS,自己打算做一个APP,刚好需要这样的效果,赞一个,希望不懂的地方,作者可以指点一下 :smile:
    LeeJay:@这个少年有故事 可以
  • 阿拉灯神钉:赞一下, 哈哈
    LeeJay:@Jon_Snow 谢谢支持:smile:
  • zl520k:写的不错
    LeeJay:@zl520k 谢谢。
  • husky_duang:666,我当初就是写到这,但是联动的关系总出错,看了您的思路豁然开朗,
    LeeJay:@固执的yu 谢谢:+1:
  • Bonew01:大神,出现的刚刚好~ :pray:
    LeeJay:@Bonew01 github长期更新,欢迎star。:smile:

本文标题:【Objective-C 联动】:两个 TableView 之间

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