外卖 App 双列表联动

2017-09-24 16:11:36 +08:00
 stephenliubp

双列表联动

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

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

一、关键思路

二、第一版代码

#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

3020 次点击
所在节点    iDev
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/393234

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX