【Objective-C 聯(lián)動】:兩個 TableView 之間的聯(lián)動,TableView 與 CollectionView 之間的聯(lián)動

前言

現(xiàn)在市面上有很多 app 都有聯(lián)動功能,有的是兩個 TableView 之間的聯(lián)動,比如美團(tuán)外賣,百度外賣,餓了么等等。有的是 TableView 與 CollectionView 之間的聯(lián)動,比如禮物說等等。

TablView 與 CollectionView 之間的聯(lián)動效果圖

本文仿造了美團(tuán)外賣和禮物說,分別實(shí)現(xiàn)了兩個 TableView 之間和 TablView 與 CollectionView 之間的聯(lián)動效果,效果圖看下面的 gif 圖。先附上 gif 圖的 demo 下載鏈接,【GitHub - OC 版】【GitHub - Swift 版】,配合 demo 一起看文章,效果會更佳。

聯(lián)動.gif

正文

一、TableView 與 TableView 之間的聯(lián)動

下面來說下實(shí)現(xiàn)兩個 TableView 之間聯(lián)動的主要思路:
先解析數(shù)據(jù)裝入模型,objectWithDictionary:是將字典轉(zhuǎn)化為模型,這個工具是我用 runtime 寫的,一行代碼解析數(shù)據(jù),具體使用方法可以參考我簡書上另一篇文章【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 關(guān)聯(lián)右邊的 TableView:點(diǎn)擊左邊的 TableViewCell,右邊的 TableView 跳到相應(yīng)的分區(qū)列表頭部。

- (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 關(guān)聯(lián)左邊的 TableView:標(biāo)記一下 RightTableView 的滾動方向,然后分別在 TableView 分區(qū)標(biāo)題即將展示和展示結(jié)束的代理函數(shù)里面處理邏輯。

  • 1.在 TableView 分區(qū)標(biāo)題即將展示里面,判斷當(dāng)前的 tableView 是 RightTableView, RightTableView 滑動的方向向上,RightTableView 是用戶拖拽而產(chǎn)生滾動的(主要判斷 RightTableView 是用戶拖拽的,還是點(diǎn)擊 LeftTableView 滾動的),如果三者都成立,那么 LeftTableView 的選中行就是 RightTableView 的當(dāng)前 section。
  • 2.在 TableView 分區(qū)標(biāo)題展示結(jié)束里面,判斷當(dāng)前的 tableView 是 RightTableView,滑動的方向向下,RightTableView 是用戶拖拽而產(chǎn)生滾動的,如果三者都成立,那么 LeftTableView 的選中行就是 RightTableView 的當(dāng)前 section-1。
// 標(biāo)記一下 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 分區(qū)標(biāo)題即將展示
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section
{
    // 當(dāng)前的 tableView 是 RightTableView,RightTableView 滾動的方向向上, RightTableView 是用戶拖拽而產(chǎn)生滾動的((主要判斷 RightTableView 用戶拖拽而滾動的,還是點(diǎn)擊 LeftTableView 而滾動的)
    if ((_rightTableView == tableView) && !_isScrollDown && _rightTableView.dragging)
    {
        [self selectRowAtIndexPath:section];
    }
}

// TableView 分區(qū)標(biāo)題展示結(jié)束
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
{
    // 當(dāng)前的 tableView 是 RightTableView,RightTableView 滾動的方向向下, RightTableView 是用戶拖拽而產(chǎn)生滾動的(主要判斷 RightTableView 用戶拖拽而滾動的,還是點(diǎn)擊 LeftTableView 而滾動的)
    if ((_rightTableView == tableView) && _isScrollDown && _rightTableView.dragging)
    {
        [self selectRowAtIndexPath:section + 1];
    }
}

// 當(dāng)拖動右邊 TableView 的時候,處理左邊 TableView
- (void)selectRowAtIndexPath:(NSInteger)index
{
    [_leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
}

這樣就實(shí)現(xiàn)了兩個 TableView 之間的聯(lián)動,是不是很簡單。

二、TableView 與 CollectionView 之間的聯(lián)動

TableView 與 CollectionView 之間的聯(lián)動與兩個 TableView 之間的聯(lián)動邏輯類似。
下面說下實(shí)現(xiàn) TableView 與 CollectionView 之間的聯(lián)動的主要思路:
還是一樣,先解析數(shù)據(jù)裝入模型。

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 關(guān)聯(lián) CollectionView,點(diǎn)擊 TableViewCell,右邊的 CollectionView 跳到相應(yīng)的分區(qū)列表頭部。

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

再將 CollectionView 關(guān)聯(lián) TableView,標(biāo)記一下 RightTableView 的滾動方向,然后分別在 CollectionView 分區(qū)標(biāo)題即將展示和展示結(jié)束的代理函數(shù)里面處理邏輯。

  • 1.在 CollectionView 分區(qū)標(biāo)題即將展示里面,判斷 當(dāng)前 CollectionView 滾動的方向向上, CollectionView 是用戶拖拽而產(chǎn)生滾動的(主要是判斷 CollectionView 是用戶拖拽而滾動的,還是點(diǎn)擊 TableView 而滾動的),如果二者都成立,那么 TableView 的選中行就是 CollectionView 的當(dāng)前 section。
  • 2.在 CollectionView 分區(qū)標(biāo)題展示結(jié)束里面,判斷當(dāng)前 CollectionView 滾動的方向向下, CollectionView 是用戶拖拽而產(chǎn)生滾動的,如果二者都成立,那么 TableView 的選中行就是 CollectionView 的當(dāng)前 section-1。
// 標(biāo)記一下 CollectionView 的滾動方向,是向上還是向下
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    static float lastOffsetY = 0;
    
    if (self.collectionView == scrollView)
    {
        _isScrollDown = lastOffsetY < scrollView.contentOffset.y;
        lastOffsetY = scrollView.contentOffset.y;
    }
}

// CollectionView 分區(qū)標(biāo)題即將展示
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
    // 當(dāng)前 CollectionView 滾動的方向向上,CollectionView 是用戶拖拽而產(chǎn)生滾動的(主要是判斷 CollectionView 是用戶拖拽而滾動的,還是點(diǎn)擊 TableView 而滾動的)
    if (!_isScrollDown && collectionView.dragging)
    {
        [self selectRowAtIndexPath:indexPath.section];
    }
}

// CollectionView 分區(qū)標(biāo)題展示結(jié)束
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(nonnull UICollectionReusableView *)view forElementOfKind:(nonnull NSString *)elementKind atIndexPath:(nonnull NSIndexPath *)indexPath
{
    // 當(dāng)前 CollectionView 滾動的方向向下,CollectionView 是用戶拖拽而產(chǎn)生滾動的(主要是判斷 CollectionView 是用戶拖拽而滾動的,還是點(diǎn)擊 TableView 而滾動的)
    if (_isScrollDown && collectionView.dragging)
    {
        [self selectRowAtIndexPath:indexPath.section + 1];
    }
}

// 當(dāng)拖動 CollectionView 的時候,處理 TableView
- (void)selectRowAtIndexPath:(NSInteger)index
{
    [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}

TableView 與 CollectionView 之間的聯(lián)動就這么實(shí)現(xiàn)了,是不是也很簡單。

最后

由于筆者水平有限,文中如果有錯誤的地方,或者有更好的方法,還望大神指出。
附上本文的所有 demo 下載鏈接,【GitHub - OC 版】【GitHub - Swift 版】,配合 demo 一起看文章,效果會更佳。
如果你看完后覺得對你有所幫助,還望在 GitHub 上點(diǎn)個 star。贈人玫瑰,手有余香。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 前言 之前用 Objective-C 寫了一篇聯(lián)動的 demo 和文章,后來有小伙伴私信我有沒有 Swfit 語言...
    LeeJay閱讀 4,824評論 25 36
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • 親愛的怡: 出差幾天,欠下不少情書,但出差的路上,抽空就學(xué)習(xí)李笑來老師的得到專欄---通往財富自由之路,沒有停止學(xué)...
    方怡閱讀 112評論 0 0
  • 1:感恩婆婆早上為我們端來可口的牛肉與米粥,嘉寶吃的飽飽去上學(xué)。 2:感恩老公早上送嘉寶上學(xué),讓我有時間整理家務(wù)。...
    嘉洋媽咪閱讀 352評論 0 0
  • 繁華落盡。我心 依然能夠聽到花落的聲音 曾經(jīng)以為 美好的事情,該發(fā)生 在唯美的雨季 淅淅瀝瀝地演奏著大地上 最溫暖...
    斜月三星洞_心容閱讀 257評論 2 3