服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS开发之级联界面(推荐界面)搭建原理

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

2021-01-24 15:04尕小天 IOS

这篇文章主要为大家详细介绍了iOS级联界面(推荐界面)搭建原理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

先看看效果图:

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) {
 }];
}

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

延伸 · 阅读

精彩推荐
  • IOS关于iOS自适应cell行高的那些事儿

    关于iOS自适应cell行高的那些事儿

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04