V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
stephenliubp
V2EX  ›  iDev

外卖 App 双列表联动

  •  
  •   stephenliubp ·
    FantasticLBP · 2017-09-24 16:11:36 +08:00 · 3009 次点击
    这是一个创建于 2378 天前的主题,其中的信息可能已经有所发展或是发生改变。

    双列表联动

    用过了那么多的外卖 App,总结出一个规律,那就是“所有的外卖 App 都有双列表联动功能”。哈哈哈哈,这是一个玩笑。

    这次我也需要开发具有联动效果的双列表。也是首次开发这种类型的 UI,记录下步骤与心得

    一、关键思路

    • 懒加载左右 2 个 UITableView
    • 根据需要自定义 Cell
    • 2 个 UITableView 加载到界面上的时候注意下部剧就好
    • 因为需要联动效果,所有左侧的 UITableView 一般是大的分类,右边的 UITableView 一般是大分类小的小分类,所以有了这样的特点
      • 左边的 UITableView 是只有 1 个 section 和 n 个 row
      • 右边的 UITableView 具有 n 个 section (这里的 section 个数恰好是左边 UITableView 的 row 数量),且每个 section 下的 row 由对应的数据源控制

    二、第一版代码

    #pragma mark -- UITableViewDelegate
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
        if (tableView == self.leftTablview) {
            return 1;
        }
        return self.datas.count;
    }
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        if (tableView == self.leftTablview) {
            return self.datas.count;
        }
        QuestionCollectionModel *model = self.datas[section];
        NSArray *questions =model.questions;
        return questions.count;
    }
    
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
        if (tableView == self.leftTablview) {
            return LeftCellHeight;
        }
        return RightCellHeight;
    }
    
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        if (tableView == self.leftTablview) {
            PregnancyPeriodCell *cell = [tableView dequeueReusableCellWithIdentifier:PregnancyPeriodCellID forIndexPath:indexPath];
            if (self.collectionType == CollectionType_Wrong || self.collectionType == CollectionType_Miss) {
                QuestionCollectionModel *model = self.datas[indexPath.row];
                cell.week = model.tag;
            }
    
            return cell;
        }
        QuestionCell *cell = [tableView dequeueReusableCellWithIdentifier:QuestionCellID forIndexPath:indexPath];
        QuestionCollectionModel *model = self.datas[indexPath.section];
        NSArray *questions =model.questions;
        QuestionModel *questionModel = questions[indexPath.row];
        cell.model = questionModel;
        return cell;
    }
    
    
    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        if (tableView == self.leftTablview) {
            NSIndexPath *indexpath = [NSIndexPath indexPathForRow:0 inSection:indexPath.row];
            [self.rightTableview scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionTop animated:YES];
        }
    }
    
    -(void)scrollViewDidScroll:(UIScrollView *)scrollView{
        if (scrollView == self.rightTableview) {
            NSIndexPath *indexpath = [self.rightTableview indexPathsForVisibleRows].firstObject;
            NSIndexPath *leftScrollIndexpath = [NSIndexPath indexPathForRow:indexpath.section inSection:0];
            [self.leftTablview selectRowAtIndexPath:leftScrollIndexpath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
    
        }
    }
    

    缺陷:虽然实现了效果,但是有缺陷。点击左侧的 UITableView,右侧的 UITableViewe 滚动到相应的位置,这是没问题的,但是滚动

    右边,需要根据右边 indexPath.section 将选中左侧相应的 indexPath。这样左侧选中的时候,又会触发右边滚动的事件,整体看上去不是很流畅。

    三、解决方案

    观察了下,发现右侧滚动的时候左侧会上下选中,所以也就是只要让右侧滚动的时候,左侧的 UITableView 单方向选中,不要滚动就好,所以由于 UITableView 也是 UIScrollview,所以在 scrollViewDidScroll 方法中判断右侧的 UITableView 是向上还是向下滚动,以此作为判断条件来让左侧的 UITableView 选中相应的行。

    且之前是在 scrollview 代理方法中让左侧的 tableview 选中,这样子又会触发左侧 tableview 的选中事件,从而导致右侧的 tablview 滚动,造成不严谨的联动逻辑

    改进后的方法:

    1. 点击左侧的 UITableView,在代理方法 didSelectRowAtIndexPath 中拿到相应的 indexPath.row ,计算出右侧 UITableView 需要滚动的 indexPath 的位置。
       [self.rightTableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
      
    2. 在 willDisplayCell 和 didEndDisplayingCell 代理方法中选中左侧 UITableView 相应的行。
    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
    
        if (tableView == self.rightTableview  && !self.isScrollDown && self.rightTableview.isDragging ) {
            [self.leftTablview selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.section inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
        }
    }
    
    
    
    -(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
        if (tableView == self.rightTableview && self.isScrollDown && self.rightTableview.isDragging) {
            [self.leftTablview selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.section+1 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
    
        }
    }
    
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
    {
        if (self.leftTablview == tableView)
        {
            [self.rightTableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
        }else{
            NSLog(@"嗡嗡嗡");
        }
    }
    
    
    #pragma mark - UIScrollViewDelegate
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
        static CGFloat lastOffsetY = 0;
    
        UITableView *tableView = (UITableView *)scrollView;
        if (self.rightTableview == tableView){
            self.isScrollDown = (lastOffsetY < scrollView.contentOffset.y);
            lastOffsetY = scrollView.contentOffset.y;
        }
    
    }
    
    效果图

    效果图

    附上 Demo:Demo

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2793 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 14:45 · PVG 22:45 · LAX 07:45 · JFK 10:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.