iOS开发之级联界面(推荐界面)搭建原理
先看看效果图:
一.整体布局
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
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; } xmgsubtagcell *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 addobject:users]; [_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) { }]; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。