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

// 调用时刻:当一个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;
 }
 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推荐界面
74hc595级联工作原理、74hc595级联原理、级联分类器原理、量子级联激光器原理、74hc595级联原理图,以便于您获取更多的相关知识。

时间: 2025-01-19 06:43:37

iOS开发之级联界面(推荐界面)搭建原理_IOS的相关文章

iOS开发系列--地图与定位源代码详解_IOS

概览 现在很多社交.电商.团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的.的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式.例如你到了一个陌生的地方想要查找附近的酒店.超市等就可以打开软件搜索周边;类似的,还有很多团购软件可以根据你所在的位置自动为你推荐某些商品.总之,目前地图和定位功能已经大量引入到应用开发中.今天就和大家一起看一下iOS如何进行地图和定位开发. 定位 地图 定位 要实现地图.导航功能,往往需要先熟悉定位功能,在iO

iOS开发 widget构建详解及实现代码_IOS

iOS开发之widget实现 前言     iOS extension的出现,方便了用户查看应用的服务,比如用户可以在Today的widgets中查看应用的简略信息,然后点击进入相关的应用界面. 暂且不表网络上现有的widget文章,本篇文章主要说明本人具体实现widget的步骤,希望能够帮助到需要实现widget的同行朋友. 图1 Today的widget展示----以支付宝为例说明 文章将依次从以下几个问题着手,进行详细说明:1.如何为现有的工程添加widget:2.如何绘制UI:3.如何调

iOS开发系列--通知与消息机制详解_IOS

概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情.iOS中通知机制又叫消息机制,其包括两类:一类是本地通知:另一类是推送通知,也叫远程通知.两种通知在iOS中的表现一致,可以通过横幅或者弹出提醒两种形式告诉用户,并且点击通知可以会打开应用程序,但是实现原理却完全不同.今天就和大家一块去看一下如何在iOS中实现这两种机制,并且在文章后面会补充通知中心的内容避免初学者对两种概念的混淆. 本地通知 本地通

iOS开发UI篇—xib的简单使用实例_IOS

这个博客申请了有一段时间了,觉得好像是该写点什么了.这篇文章主要是关于一些xib的简单的用法,希望可以帮助到刚刚使用xib的新手们. 什么是xib? xib能做什么? 用来描述软件界面的文件. 如果没有xib,所有的界面都需要通过代码来手动创建. 有了xib以后,可以在xib中进行可视化开发,然后加载xib文件的时候,系统自动生成对应的代码来创建界面. 与xib类似的还有storyboard文件.xib和storyboard的比较,一个轻量级一个重量级. 共同点: 都用来描述软件界面.都用Int

IOS开发之路--C语言数组和字符串_IOS

概览 数组在C语言中有着特殊的地位,它有很多特性,例如它的存储是连续的,数组的名称就是数组的地址等.而在C语言中是没有String类型的,那么如果要表示一个字符串,就必须使用字符串数组.今天主要就介绍如下三个方面: 一维数组 多维数组 字符串 一维数组 一维数组操作比较简单,但是需要注意,数组长度必须是固定的,长度不能使用变量进行初始化:如果声明的同时进行赋值则数组长度可以省略,编译器会自动计算数组长度:同时数组不能先声明再一次性赋值(当然可以对每个元素一一赋值). #include <stdi

iOS开发中使用UIWebView 屏蔽 alert警告框_IOS

 如果是网页内容里面的alert,我们可以等网页加载完毕,也就是在webViewDidFinishLoad中执行下面的js代码,就可以屏蔽alert了 [myWebView stringByEvaluatingJavaScriptFromString:@"window.alert=null;"]; 但上面的方法对于网页onLoad事件里面的alert就不起作用了 解决方法就是给UIWebView添加一个类别: 给工程添加JavaScriptAlert.h @interface UIWe

IOS开发使用KeychainItemWrapper 持久存储用户名和密码_IOS

首先从官网下载 KeychainItemWrapper.h KeychainItemWrapper.m 将这两个文件导入项目中 不过该文件是手动释放的 所以要使用这个文件需要先做一些处理: 如果要使用KeychainItemWrapper.h类 在CompileSources中选中该类 添加-fno-objc-arc 接下来直接上代码: KeychainItemWrapper *keychain=[[KeychainItemWrapper alloc] initWithIdentifier:@"

IOS 开发APP之关于时间处理详细介绍_IOS

IOS 时间处理 做App避免不了要和时间打交道,关于时间的处理,里面有不少门道,远不是一行API调用,获取当前系统时间这么简单.我们需要了解与时间相关的各种API之间的差别,再因场景而异去设计相应的机制. 时间的形式 在开始深入讨论之前,我们需要确信一个前提:时间是线性的.即任意一个时刻,这个地球上只有一个绝对时间值存在,只不过因为时区或者文化的差异,处于同一时空的我们对同一时间的表述或者理解不同.这个看似简单明了的道理,是我们理解各种与时间相关的复杂概念的基石.就像UTF-8和UTF-16其

IOS图片无限轮播器的实现原理_IOS

首先实现思路:整个collectionView中只有2个cell.中间始终显示第二个cell. 滚动:向前滚动当前cell的脚标为0,向后滚动当前的cell脚标为2.利用当前cell的脚标减去1,得到+1,或者-1,来让图片的索引加1或者减1,实现图片的切换. 声明一个全局变量index来保存图片的索引,用来切换显示在当前cell的图片. 在滚动前先让显示的cell的脚标变为1.代码viewDidLoad中设置 完成一次滚动结束后,代码再设置当前的cell为第二个cell(本质上就是让当前显示的