咱们常常使用美团外卖、饿了么、口碑等外卖软件点餐,几乎全部的外卖软件所展现的商品类别都无一例外,采用双列表的形式呈现,商品的分类,以及对商品的下单操做。咱们拿美团外卖为例,截图以下:git
暂时忽略头部视图,只关注下面的商品分组列表。github
在开始以前,咱们首先应该思考其实现的流程和可能遇到的问题!首先映入眼帘的是左侧的商品分类列表,以及右侧的分区展示的商品列表。因此:布局
//分区头视图将要显示 - (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;
两个tableView布局、delegate的链接 及 关联代码测试
_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" : @[@"甜点组合", @"毛肚", @"菌汤"] }, ];
//分区数 - (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]; }
//单元格内容 - (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; }
测试运行工程能够看到一个简单的双列表已经呈如今你的面前,以下图所示:优化
//分区头视图 - (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的绘制完成了,接下来解决联动的问题吧。动画
首先定义一个BOOL类型的变量_relate 来标记左侧列表是否滚动,在viewDidLoad和下面的代理中把_relate置为YES:spa
#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地址:code
项目地址: