级联界面(推荐关注)

示例图片

一.整体布局

1.项目需求
点击左边cell,右边的cell数据更新

2.界面搭建

2.1交给两个控制器管理比较麻烦,点击一个控制器需要通知另外一个控制器

2.2因此交给一个控制器管理比较好

2.3用xib搭建,左右各放一个tableView就可以了

3.开发顺序
先做左边的tableView,再做右边的,因为右边的数据是根据左边变化的

二.左边tableView界面搭建

1.自定义cell
左边一个指示器欧一个view 中间位置用label

2.设置数据源
两个tableView设置同一个控制器为数据源和代理,实现方法的时候要先判断tableView的类型

3.请求数据,查看接口文档

4.字典转模型

5.显示数据

6.运行发现一个tableView顶部被挡住,另一个没被挡住,为什么?
苹果默认只给界面上一个scrollView设置额外滚动区域
只需要取消自动设置的额外滚动区域,自己手动设置就可以了

7.选中cell,让cell的指示器显示

7.1 怎么实现?
监听cell选中,选中就让指示器显示

7.2 但是还要监听取消选中,把指示器隐藏

7.3 怎么同时监听一个cell被选中,另一个cell取消选中?
cell自己有一个方法可以同时监听

  // 调用时刻:当一个cell选中的时候就会调用,并且一个cell取消选中的时候也会调用
- (void)setSelected:(BOOL)selected animated:(BOOL)animated

7.4 cell不需要选中状态

 self.selectionStyle = UITableViewCellSelectionStyleNone;

8.点击左边cell的时候,请求右边tableView的数据
监听左边cell的点击,然后发送网络请求,请求右边的数据

三.右边tableView界面搭建

1.xib复用
xib也能复用,当两个界面的xib一样时,可以用同一个xib,只要给xib传递不同的模型即可

2.右边tableView业务逻辑

3.点击左边的cell,发送网络请求,请求右边的数据

4.请求数据,查看接口文档

4.1发现有一个参数category_id 需要根据左边服务器返回的id 来加载右边的数据,所以,我们在左边tableView的模型中要再加一个id属性

4.2复用模型,模型也能复用,只需要在原来模型中添加需要数据的属性即可

5.展示数据,点击左边cell,右边就显示对应的数据

四.整体数据优化

1.默认选中左边的第0个cell

- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition;

2.默认选中第0个cell.写在哪里?

2.1写在viewDidLoad里面?
不可以,这个时候还没有数据

2.2要写在数据加载成功,而且刷新表格之后
刷新代码:

 [self.categoryTableView reloadData];
    // 默认选中第0个cell
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.categoryTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];

3.手动选中左边第0个cell,发现右边数据没刷新

3.1为什么?
因为- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath方法必须用户手动点击cell才会触发

3.2怎么解决?
自己去调用这个方法,写在默认选中第0个cell后边就可以了

 [self tableView:self.categoryTableView didSelectRowAtIndexPath:indexPath];

4.数据优化

4.1每次点击左边cell,右边cell都需要发送请求获得数据,消耗性能

4.2如果加载过一次,就保存起来,下次就不用再加载了。

4.3保存到哪里?
保存到对应的分类模型的用户数组里面
在分类tableView的模型中定义一个用户数组,保存左边cell对应的右边的tableView的数据

4.4 怎么拿到左边cell对应的一组模型?
请求右边数据的时候,左边对应的cell一定是被选中的,通过记录选中cell对应的模型,就能拿到这个模型

4.5 在选中左侧cell的方法中,先判断模型中user数组(右边对应的数据数组)是否有值
如果有值,直接刷新表格,然后return,就不在发送网络请求
如果没有,就发送网络请求,请求成功后,保存数据,刷新表格,展示数据

五.上下拉刷新

1.项目需求
右边的tableView需要上下拉刷新功能

2.怎么实现上下拉刷新?
使用 MJRefresh框架

3.下拉刷新,直接加载最新数据,覆盖掉原来的就可以了
我们原本就是直接用模型中的数组,覆盖掉原来的数据,所以就不用做移除原来数据的处理了

4.上拉刷新业务逻辑

4.1上拉刷新,需要加载更多的数据,怎么加载更多的数据?
需要一个参数(page 或 ID),来获得更多的数据

4.2这个参数(page)服务器会返回,我们需要记录一下,记录到哪里?
应该记录到左边tableView的模型中,请求更多数据的时候,从模型中取出这个参数发送请求

4.3 下拉刷新的时候,要对page参数还原
把page重置为1,否则下拉刷新,会加载其它页码的数据,到值数据错乱

4.4 加载更多数据成功的时候,我们就要对page +1,因为记录的page 会作为下次请求参数传递
注意:只要请求数据,请求成功的时候,就要对page + 1

4.5 上拉刷新,对数据的处理
上拉刷新,需要把原来的数据和新加载的数据一起显示

4.6 怎么一起显示?
用数组保存加载的更多数据,把这个数组中的元素添加到原来数据数组中

4.7,怎么把一个数组中的元素,添加到另一个数组中?
通过- (void)addObject:(ObjectType)anObject;方法?
不可以,这个方法会把整个数组作为一个元素,添加到另一个数组中

  [_selectCategoryItem.users addObject:users];

4.8.那用哪个方法?

 - (void)addObjectsFromArray:(NSArray<ObjectType> *)otherArray;

这个方法会把数组中的每一个元素取出来,添加到另一个数组中

5.上拉刷新细节处理

5.1 当没有更多数据的时候,需要隐藏上拉刷新控件

5.2 怎么隐藏?
拿到控件设置hidden属性 self.userTableView.mj_footer.hidden

5.3隐藏的条件是什么?
需要判断当前用户组,有没有更多用户
5.4 怎么判断?
服务器返回的数据有一个 total_page属性,如果当前页>= total_page就没有更多数据

5.5需要保存 total_page属性,保存到哪里?
保存到左边tableView的模型中,每次请求成功,就把total_page属性保存到对应的用户组中

5.6 在刷新表格的时候,当前的page属性是 当前页数+ 1 的值
所以设置上拉刷新隐藏的条件应该是 : page > total_page

5.7 隐藏代码写在哪里?
写在刷新表格之后,MJ刷新框架每次刷新完数据,会自动判断是否隐藏,一定要在刷新方法后设置才有用
5.8 每次点击左边cell的时候,也要判断是否隐藏上拉刷新控件,为什么?
有可能数据只有一页,不判断的话,就会显示上拉刷新控件,去刷新的时候,拿不到更多数据

源代码

- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"推荐关注";
self.automaticallyAdjustsScrollViewInsets = NO;
_categoryTableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
_userTableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
// 分类tableView注册cell
[_categoryTableView registerNib:[UINib nibWithNibName:@"XMGCategoryCell" bundle:nil] forCellReuseIdentifier:categoryID];
// 用户tableView注册cell
[_userTableView registerNib:[UINib nibWithNibName:@"XMGSubTagCell" bundle:nil] forCellReuseIdentifier:userID];
// 请求分类数据
[self loadCategoryData];
// 添加上下拉刷新
[self setupRefreshView];
}
- (void)setupRefreshView
{
// 下拉刷新
// 当松手,并且下拉刷新完全显示的时候,就会触发下拉刷新
MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewUserData)];
header.automaticallyChangeAlpha = YES;
self.userTableView.mj_header = header;

// 上拉刷新
MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreUserData)];
footer.automaticallyHidden = YES;
self.userTableView.mj_footer = footer;
}

- (void)loadCategoryData
{
AFHTTPSessionManager *mgr = [AFHTTPSessionManager xmg_manager];

NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"a"] = @"category";
parameters[@"c"] = @"subscribe";

[mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) {
    NSArray *dictArr = responseObject[@"list"];

    _categorys = [XMGCategoryItem mj_objectArrayWithKeyValuesArray:dictArr];

    [self.categoryTableView reloadData];

    // 默认选中第0个cell
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.categoryTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];

    [self tableView:self.categoryTableView didSelectRowAtIndexPath:indexPath];

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == _categoryTableView) { // 显示分类TableView
    return _categorys.count;
}
return _selectCategoryItem.users.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == _categoryTableView) { // 显示分类TableView
    XMGCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:categoryID];
    cell.item = _categorys[indexPath.row];
    return cell;
}
 XTSubTagCell *cell = [tableView dequeueReusableCellWithIdentifier:userID];
 cell.user =  _selectCategoryItem.users[indexPath.row];
return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == _categoryTableView) {
    return 44;
}
return 60 + 1;
}
// 点击cell就会调用
// 必须用户手动点击cell才会触发
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == _categoryTableView) {
    // 记录选中分类模型
    _selectCategoryItem = _categorys[indexPath.row];
    // 点击分类cell
    // 判断之前有没有数据
    if (_selectCategoryItem.users.count) {
        [self.userTableView reloadData];
        self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page;
        return;
    }
    // 请求右边用户数据
    [self loadNewUserData];
}
}

// 没有更多数据的时候,隐藏上拉刷新控件
// 判断当前分类用户组 有没有更多用户组
// 加载更多用户数据
- (void)loadMoreUserData
{
[self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)];

NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"a"] = @"list";
parameters[@"c"] = @"subscribe";
parameters[@"category_id"] = _selectCategoryItem.id;
parameters[@"page"] = @(_selectCategoryItem.page);

[self.mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) {

    [self.userTableView.mj_footer endRefreshing];

    _selectCategoryItem.page++;
    NSArray *dictArr = responseObject[@"list"];

    NSArray *users = [XMGUserItem mj_objectArrayWithKeyValuesArray:dictArr];

    // 取出数组中所有元素,添加到新数组
    [_selectCategoryItem.users addObjectsFromArray:users];

    [self.userTableView reloadData];

    // 控制上拉控件是否显示,一定要在reloadData之后
    self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}

// 加载更新用户数据
- (void)loadNewUserData
{
_selectCategoryItem.page = 1;
[self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)];

NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"a"] = @"list";
parameters[@"c"] = @"subscribe";
parameters[@"category_id"] = _selectCategoryItem.id;

[self.mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) {

    _selectCategoryItem.page++;

    // 记录当前分类总页码数
    _selectCategoryItem.total_page = [responseObject[@"total_page"] integerValue];

    // 结束刷新
    [self.userTableView.mj_header endRefreshing];

    NSArray *dictArr = responseObject[@"list"];

    _selectCategoryItem.users = [XMGUserItem mj_objectArrayWithKeyValuesArray:dictArr];

    [self.userTableView reloadData];

    self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page;

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容