文档章节

iOS 经典的外卖列表 双Table联动

魔笛GNR
 魔笛GNR
发布于 2017/05/05 17:35
字数 1505
阅读 54
收藏 0
点赞 0
评论 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
博文 73
码字总数 36902
作品 0
郑州
iOS工程师
iOS小白点效果、打地鼠游戏、轮播效果、画板、富文本编辑器等源码

iOS精选源码 美团外卖商家点菜页面(http://www.code4app.com/thread-29383-1-1.html) 板书录制演示程序(http://www.code4app.com/thread-29384-1-1.html) 分类轮播、直播间礼物列表轮播 -- O...

sunnyaigd ⋅ 04/24 ⋅ 0

Windows 95 风格的 iOS UI 组件集 - ClassicKit

ClassicKit 是一个适用于 iOS 的 Windows 95 经典风格 UI 组件集。 ClassicKit 将 Windows 95 的风格带入了 iOS 应用中,大部分图标等资源都来自实际安装的 Windows 95,效果感觉就像在 iOS ...

匿名 ⋅ 05/17 ⋅ 0

怀旧,开源项目将 Windows 95 画风带到 iOS 上

开发者天马行空的想法总是能够给人带来惊喜,日前有开发者写了一个适用于 iOS 的 Windows 95 经典风格 UI 组件集 ClassicKit,并将其开源在 GitHub 上。 该项目将 Windows 95 的风格带入了 ...

雨田桑 ⋅ 05/17 ⋅ 8

【AR】开始使用Vuforia开发iOS(2)

原 设置iOS开发环境 安装Vuforia iOS SDK 如何安装Vuforia iOS示例 编译并运行Vuforia iOS示例 支持iOS金属 iOS 64位迁移 设置iOS开发环境 适用于iOS的Vuforia引擎目前支持运行iOS 9及更高版...

lichong951 ⋅ 06/11 ⋅ 0

iOS逆向工程- 学习整理(工具详解)

前言 一、逆向工程的要求 具备丰富的 iOS 开发经验 最好能非常熟悉 iOS 设备的硬件构成,iOS 系统的运行原理。 拿到任意一个 App 之后能够大致推断出它的项目规模和使用的技术,比如它的MVC模...

_小迷糊 ⋅ 05/11 ⋅ 0

IOS12开发者预览版第二版使用评测

     手头有一部iPhone 6s Plus,因为之前看同学和朋友们升到IOS 11后手机变得非常卡顿所以没有升级。最近偶然看到IOS 12针对旧机型做了性能优化,号称提升70%相机打开速度、50%键盘显示...

张旭乾 ⋅ 昨天 ⋅ 0

iOS高仿QQ侧滑控件、下载框架、动画效果、扫一扫、颜色变化、K线图等源码

iOS精选源码 仿京东"加入购物车"转场动画(http://www.code4app.com/thread-28162-1-1.html) ColorTool(颜色转换)(http://www.code4app.com/thread-29256-1-1.html) Swift 专业版K线(http://w......

sunnyaigd ⋅ 04/17 ⋅ 0

3.3 Js、App和缓存---熊孩子、篮子和仓库

前端组合:熊孩子、村姑、化妆师   上次在这提到村姑和化妆师的故事,其实村姑背后有个大家族。上次教大家如何用代码做自我介绍,其实用到了JavaScript(简称js)。   html只是个静态页面...

产品经理的技术课堂 ⋅ 04/26 ⋅ 0

HDU ~ 6297 ~ CCPC直播 (模拟,输出格式控制)

思路:模拟就行了,注意Running和RTE的开头字母一样。 iomanip是I/O流控制头文件,就像printf的格式化输出一样。 以下是一些常用的: dec 置基数为10 相当于"%d" hex 置基数为16 相当于"%X" oc...

zscdst ⋅ 05/29 ⋅ 0

经典Win95风格的iOS UI 组件集 ClassicKit

ClassicKit是 Windows 95 经典风格的iOS 开源UI 组件集。图标,窗框的资源都来自Windows 95。每个组件都像 UIKit 的对应组件一样使用,例如,CKButton 像 UIButton 一样响应手势事件。同时这...

marsdream ⋅ 05/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 58分钟前 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

VS中使用X64汇编

需要注意的是,在X86项目中,可以使用__asm{}来嵌入汇编代码,但是在X64项目中,再也不能使用__asm{}来编写嵌入式汇编程序了,必须使用专门的.asm汇编文件来编写相应的汇编代码,然后在其它地...

simpower ⋅ 今天 ⋅ 0

ThreadPoolExecutor

ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ......

4rnold ⋅ 昨天 ⋅ 0

Java正无穷大、负无穷大以及NaN

问题来源:用Java代码写了一个计算公式,包含除法和对数和取反,在页面上出现了-infinity,不知道这是什么问题,网上找答案才明白意思是负的无穷大。 思考:为什么会出现这种情况呢?这是哪里...

young_chen ⋅ 昨天 ⋅ 0

前台对中文编码,后台解码

前台:encodeURI(sbzt) 后台:String param = URLDecoder.decode(sbzt,"UTF-8");

west_coast ⋅ 昨天 ⋅ 0

实验楼—MySQL基础课程-挑战3实验报告

按照文档要求创建数据库 sudo sercice mysql startwget http://labfile.oss.aliyuncs.com/courses/9/createdb2.sqlvim /home/shiyanlou/createdb2.sql#查看下数据库代码 代码创建了grade......

zhangjin7 ⋅ 昨天 ⋅ 0

一起读书《深入浅出nodejs》-node模块机制

node 模块机制 前言 说到node,就不免得提到JavaScript。JavaScript自诞生以来,经历了工具类库、组件库、前端框架、前端应用的变迁。通过无数开发人员的努力,JavaScript不断被类聚和抽象,...

小草先森 ⋅ 昨天 ⋅ 0

Java桌球小游戏

其实算不上一个游戏,就是两张图片,不停的重画,改变ball图片的位置。一个左右直线碰撞的,一个有角度碰撞的。 左右直线碰撞 package com.bjsxt.test;import javax.swing.*;import j...

森林之下 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部