文档章节

【iOS开发实战】经典的外卖列表(双Table联动)

魔笛GNR
 魔笛GNR
发布于 2017/05/05 17:35
字数 1566
阅读 79
收藏 0

        我们经常使用美团外卖、饿了么、口碑等外卖软件点餐,几乎所有的外卖软件所展示的商品类别都无一例外,采用双列表的形式呈现,商品的分类,以及对商品的下单操作。我们拿美团外卖为例,截图如下:

暂时忽略头部视图,只关注下面的商品分组列表。

思路:

        在开始之前,我们首先应该思考其实现的流程和可能遇到的问题!首先映入眼帘的是左侧的商品分类列表,以及右侧的分区展现的商品列表。所以:

  • 我们至少需要两个tableView,单纯的放上两个tableView还不能满足实际需求。
  • 拿美团外卖这个界面来看,点击左侧商品分类,右侧的商品列表会将该分类的分区第一条数据滚动至右侧tableView的顶部。用户滚动右侧列表,左侧分类列表会随之高亮显示相应的分类标签。可以通过UITableViewDelegate的协议,即分区头、脚视图的显示周期,以及UIScrollViewDelegate的相应协议来实现左侧和右侧列表的联动。并结合tableView的滚动方法实现双列表的联动效果。
  • //分区头视图将要显示
    - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
    //分区脚视图已经结束显示
    - (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section
    //以及结束减速
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
    //滚动至某一行
    - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;

     

实践:

1、创建两个tableView:

  •     为了尽可能简单实现,我们采用StoryBoard来创建这两个tableView(当然你也可以使用代码来创建),左侧tableView宽度为固定宽度100,右侧tableView宽度为剩余屏幕宽度,这里采用自动布局技术约束(这里不再赘述其实现过程)。如下图所示:


两个tableView布局、delegate的连接 及 关联代码

2、模拟分类的商品数据

_relate = YES;    
goodsList = @[
                      @{@"title" : @"精选特卖",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤", @"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title" : @"饭后(含有茶点)",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title" : @"茶点(含有茶点)",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title" : @"素材水果拼盘",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤",]
                        },
                      @{@"title" : @"水果拼盘生鲜果",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤",]
                        },
                      @{@"title" : @"拼盘",
                        @"list" : @[@"甜点组合"]
                        },
                      @{@"title" : @"烤鱼盘",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title" : @"饮料",
                        @"list": @[@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤",@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title": @"小吃",
                        @"list": @[@"甜点组合", @"毛肚"]
                        },
                      @{@"title" : @"作料",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      @{@"title" : @"主食",
                        @"list" : @[@"甜点组合", @"毛肚", @"菌汤"]
                        },
                      ];


3、绘制两tableView

  •     首先别忘了让ViewController遵守tableView的delegate:

  • 分区数、行数的实现:
//分区数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    if (tableView==_leftTableView) {
        return 1;
    }
    return goodsList.count;
}

//行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if (tableView==_leftTableView) {
        return goodsList.count;
    }
    return [[goodsList[section] objectForKey:@"list"] count];
}
  • 单元格内容的实现,这里只使用系统简单的cell风格,左侧cell高度默认80,右侧默认100,如下图所示:
//单元格内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell==nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    if (tableView==_leftTableView) {
        //分类标题
        cell.textLabel.text = [goodsList[indexPath.row] objectForKey:@"title"];
    }else{
        //商品标题
        cell.textLabel.text = [[goodsList[indexPath.section] objectForKey:@"list"] objectAtIndex:indexPath.row];
    }
    return cell;
}

//行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if (tableView==_leftTableView) {
        return 80;
    }
    return 100;
}

    测试运行工程可以看到一个简单的双列表已经呈现在你的面前,如下图所示:

  • 绘制分区头视图,高度设置为30:
//分区头视图
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
    if (tableView==_rightTableView) {
        UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _rightTableView.bounds.size.width, 30)];
        view.backgroundColor = [UIColor colorWithWhite:0.9 alpha:0.9];
        UILabel * label = [[UILabel alloc] initWithFrame:view.bounds];
        [view addSubview:label];
        label.text = [goodsList[section] objectForKey:@"title"];
        return view;
    }
    return nil;
}

//分区头视图高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
    if (tableView==_leftTableView) {
        return CGFLOAT_MIN;
    }
    return 30;
}

//脚视图高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
    if (tableView == self.leftTableView) {
        return 0;
    } else {
        //重要
        return CGFLOAT_MIN;
    }
}

       

    至此,两个tableView的绘制完成了,接下来解决联动的问题吧。

4、两个tableView的联动

    首先定义一个BOOL类型的变量_relate 来标记左侧列表是否滚动,在viewDidLoad和下面的代理中把_relate置为YES:

#pragma mark - UIScrollViewDelegate
//已经结束减速
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    _relate = YES;
}
  • 实现点击左侧单元格,右侧列表自动滚动到相应分区
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (tableView == _leftTableView) {
        _relate = NO;
        //选择该行,并自动滚动至列表中心区域
        [self.leftTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
        //右侧滚动至相应分区
        [self.rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }else {
        //取消选中
        [self.rightTableView deselectRowAtIndexPath:indexPath animated:NO];
        
    }
}

 

  • 实现滚动右侧列表,左侧列表自动选中相应分区标题
//分区头即将显示
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    if (_relate) {
        //获取显示在最顶部的cell的分区数
        NSInteger topCellSection = [[[tableView indexPathsForVisibleRows] firstObject] section];
        if (tableView == self.rightTableView) {
            //滚动该分区对应的标题至列表靠近中部区域
            [self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForItem:topCellSection inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
        }
    }
}

//分区头已经结束显示
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section {
    if (_relate) {
        //获取显示在最顶部的cell的分区数
        NSInteger topCellSection = [[[tableView indexPathsForVisibleRows] firstObject] section];
        if (tableView == self.rightTableView) {
            //滚动该分区对应的标题至列表靠近中部区域
            [self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForItem:topCellSection inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
        }
    }
}

至此一个简单实用的经典双列表联动效果已经实现了!

Demo地址:

https://github.com/ly918/Demos

 

优化:


另外,我对其做了进一步的扩展优化,如加入购物车的动画,导航渐变等,效果图如下所示:

项目地址:

https://github.com/ly918/TakeawayList-ShoppingCart

© 著作权归作者所有

共有 人打赏支持
魔笛GNR
粉丝 6
博文 82
码字总数 43485
作品 0
郑州
iOS工程师
Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序

Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序。自从Xamarin问世后,C#的作用就发生了很大的变化。它不仅可以编写关于Windows...

大学霸
2014/10/23
0
0
100%移植阿里云移动测试技术_竟仅需1周?!——移动测试专有云(3)——内容详解

一、自动化测试服务 Android兼容性测试 Android兼容性测试旨在帮助解决Android应用在不同真机机型上的各类兼容性问题,包括 Crash/ANR分析、6项性能分析、UI检测、3个版本的覆盖安装检测等。...

乐乎无趣
2017/11/02
0
0
iOS Simulator功能介绍关于Xamarin IOS开发

iOS Simulator功能介绍关于Xamarin IOS开发 iOS Simulator功能介绍 在图1.38所示的运行效果中,所见到的类似于手机的模型就是iOS Simulator。在没有iPhone或iPad设备时,可以使用iOS Simulat...

大学霸
2015/04/17
0
0
Xamarin iOS开发实战1.1.3Xamarin版本

Xamarin iOS开发实战1.1.3Xamarin版本 Xamarin提供了免费版和付费版。免费版本包含Xamarin Studio服务。付费版本分为普通版299美元/年、商业版999美元/年和企业版1899美元/年。开发者可以根据...

大学霸
2014/12/17
0
0
Xamarin iOS编写第一个应用程序创建工程

Xamarin iOS编写第一个应用程序创建工程 在Xcode以及Xamarin安装好后,就可以在Xamarin Studio中编写程序了。本节将主要讲解在Xamarin Studio中如何进行工程的创建以及编写代码等内容Xamarin...

大学霸
2015/01/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Shiro | 实现权限验证完整版

写在前面的话 提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。 Shiro框架 1、Shiro是基于Apache开源的强大灵活的开源...

冯文议
今天
1
0
linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
今天
2
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
今天
3
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
15
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部