iOS开发之级联界面(推荐界面)搭建原理

时间:2022-09-19 19:42:06

先看看效果图:

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自己有一个方法可以同时监听 

?
1
2
// 调用时刻:当一个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要写在数据加载成功,而且刷新表格之后 

刷新代码: 

?
1
2
3
4
[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的时候,也要判断是否隐藏上拉刷新控件,为什么?
有可能数据只有一页,不判断的话,就会显示上拉刷新控件,去刷新的时候,拿不到更多数据

源代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
- (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) {
 }];
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。