提供思路如下:
- 分析界面及接口
- 用 MVC 设计模式来实现(其实核心点都在下面5)
- 创建内外层 Model 并绑定两者 Model
- 两者 Cell 布局的实现 (便于后期界面的快速更改)
- 在外层控制器内进行逻辑操作并请求数据的分区及每个分区行数的处理.
5.1 创建一个保存数据的数组timeList, 这个数组是外层列表数组(分区时间数组)
5.2 拿到数组后, 就可以考虑分区数目及当前分区下 cell 数目
5.3 创建header的界面及数据
5.4 找到相对应的分区去带回内层数组 - 跳转控制器内层数组的传值简单实现.
Step1. 分析界面及接口
首先我们先分析这个界面:
很明显这是一个 tableView 的多分区多cell的布局.
这样就好办许多,截取一个分区来做说明.
很明显的能看出来, 这必须只能是 Model 套 Model 了. 分析下接口:
接口分析
Step2. 用 MVC 设计模式来实现.
大致思路有了,那我们接着该整理 MVC 了.
简单来说, 就是创建两套 MVC: 外层盘点记录的 MVC 与 内层盘点详情的 MVC(其中内层的还包括外层盘点记录下的每个当前分区下的记录 cell 详情).
看起来是两套 MVC, 由于我们内层的两个数组里字典数据是一样的模型, 我就共用了一套内层 M, 其实是三部分的交错使用.
看个点击分区跳转到盘点详情的 MVC 界面:
盘点详情.png
Step3. 创建内外层 Model 并绑定两者 Model
由于只提供思路,就只放核心部分代码.
先创建内层的 子Model(YYPRecordDetailsModel)就和一般正常的 Model 创建一样就好,
// 时间
@property (nonatomic, copy) NSString *dotime;
// 商品名称
@property (nonatomic, copy) NSString *commodityName;
// 商品金额
@property (nonatomic, assign) float commodityAmt;
// 实际盘点数量
@property (nonatomic, assign) NSInteger inventoryNum;
// 库存数量
@property (nonatomic, assign) NSInteger stockNum;
接着创建外层的 一级Model(YYPInventoryRecordModel), 除了几个基本单元素外, 两个内层数组是子 Model 不要忘了要放进去.
// 详情全部数据
@property (nonatomic, strong) NSArray *inventoryList;
// 亏盈数据
@property (nonatomic, strong) NSArray *inventoryList2;
最后在外层 Model 的. m 里实现两者的绑定.
+ (NSDictionary *)objectClassInArray {
return @{@"inventoryList" : [YYPRecordDetailsModel class], @"inventoryList2" : [YYPRecordDetailsModel class]};
}
Step4. 两者 Cell 布局的实现
页面都是根据产品和 UI 确定, 我们单独写UITableViewCell, 好处就是便于后期界面的快速更改.
可以根据自己的界面来写. 我这里就记录下 Model 赋值. 一定切记,选择相对应的 Model. 我这里展示分区下的 cell 就相对应的是子Modle.
// model赋值
- (void)setModel:(YYPRecordDetailsModel *)model {
_model = model;
// 商品名称
= [NSString stringWithFormat:@"%@",
// 单价
= [NSString stringWithFormat:@"¥%.2f",
// 盈亏
if (model.inventoryNum >= model.stockNum) {
self.result.textColor = YYPGrayTitleColor;
if (model.inventoryNum > model.stockNum) {
self.result.text = [NSString stringWithFormat:@"+%ld", (model.inventoryNum - model.stockNum)];
} else {
self.result.text = @"0";
}
} else if (model.inventoryNum < model.stockNum) { // 亏
self.result.textColor = YYPRedTitleColor;
self.result.text = [NSString stringWithFormat:@"-%ld", (model.stockNum - model.inventoryNum)];
}
}
Step5. 在外层控制器内进行逻辑操作并请求数据的分区及每个分区行数的处理.
其实这里就是重点核心部分了!我要划重点啦~
5.1 看盘点记录界面, 就是一个TableView, 那么我们创建一个保存数据的数组timeList, 这个数组是外层列表数组(分区时间数组).
@property (nonatomic, strong) NSMutableArray *timeList;
- (NSMutableArray *)timeList {
if (!_timeList) {
_timeList = [NSMutableArray array];
}
return _timeList;
}
PS: 切记!!!千万不要再创建内层数组,容易把自己绕糊涂, 有了外层数组,需要内层数组数据时是可以通过外层数组 timeList 去获取的.譬如行数据:model.inventoryList2[indexPath.row]
5.2 拿到数组后, 就可以考虑分区数目及当前分区下 cell 数目
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
self.tableView.mj_footer.hidden = self.timeList.count == 0;
return self.timeList.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
YYPInventoryRecordModel *model = self.timeList[section];
return model.inventoryList2.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YYPInventoryRecordDetailCell *detailCell = [YYPInventoryRecordDetailCell cellWithTableView:tableView];
if (self.timeList.count) { // 有时候传值为nil
YYPInventoryRecordModel *model = self.timeList[indexPath.section];
YYPRecordDetailsModel *detailModel = model.inventoryList2[indexPath.row];
detailCell.model = detailModel;
}
return detailCell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
return YYPInventoryRecordDetailCellHeight;
}
5.3 这个时候盘点记录的 cell详情出来了, 接着考虑header的界面及数据.
在viewForHeaderInSection
里创建
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section ;
赋值取当前一级 Model 下字段: [[_timeList objectAtIndex:section] valueForKey:@"dotime"]];
dotime.text = [NSString stringWithFormat:@"%@", [[_timeList objectAtIndex:section] valueForKey:@"dotime"]];
分区Header内容也是由外层 Model 赋值的, 返回每个分区的内容self.timeList[section]
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return self.timeList[section];
}
5.4 由于我们还有下个跳转页面,同时还是取当前接口数据, 这个时候我们就要找到相对应的分区去跳转即可.
在cell 点击跳转相对来说方便的多,因为这个有系统方法didSelectRowAtIndexPath
.只需要找到相对应分区self.timeList[indexPath.section]
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
YYPInventoryRecordModel *model = self.timeList[indexPath.section];
YYPRecordDetailsController *vc = [[YYPRecordDetailsController alloc] init];
vc.inventoryList = model.inventoryList;
[self.navigationController pushViewController:vc animated:YES];
}
若是header 上也要实现按钮跳转, 可以在viewForHeaderInSection
方法里的按钮上加个标记与header的分区section一致, 然后找到相对应分区self.timeList[sender.tag]就好.
inIcon.tag = section;
实现跳转方法
- (void)btnClick:(UIButton *)sender {
// 用 tag 来标记section
YYPInventoryRecordModel *model = self.timeList[sender.tag];
YYPRecordDetailsController *vc = [[YYPRecordDetailsController alloc] init];
vc.inventoryList = model.inventoryList;
[self.navigationController pushViewController:vc animated:YES];
}
5.5 最后就是请求了.
拿到最外层的数组数据就好.
[weakSelf.timeList removeAllObjects];
NSArray *currentPageArray = [YYPInventoryRecordModel loadInventoryRecordInfoFromJson:json[@"data"]];
[weakSelf.timeList addObjectsFromArray:currentPageArray];
至于上拉加载更多数据/下拉刷新新数据 及 网络不佳状态重新加载获取请求这些都是按自己项目需求添加的. 这里就不一一展示了.
Step6. 跳转控制器内层数组的传值简单实现.
这其实就是一个简单的正常的 MVC. 就是不需要请求接口,就是正向传值带回来一个数组.
.h 里露出一个属性便于传值.
// 详情全部数据
@property (nonatomic, strong) NSArray *inventoryList;
在. m 里创建一个保存接收数据的可变数据
// 详情全部数据
@property (nonatomic, strong) NSMutableArray *goodList;
需要加载inventoryList数据带回到goodList.
#pragma mark - 懒加载
- (NSMutableArray *)goodList {
if (!_goodList) {
_goodList = [NSMutableArray array];
[_goodList addObjectsFromArray:_inventoryList];
}
return _goodList;
}
简单的Table view data source方法:
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.goodList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YYPRecordDetailsCell *cell = [YYPRecordDetailsCell cellWithTableView:tableView];
if (self.goodList.count) { // 有时候传值为nil
YYPRecordDetailsModel *model = self.goodList[indexPath.row];
cell.model = model;
}
return cell;
}
这个时候,完整测试下效果吧:
盘点.gif