iOS MVVM在UITableView中的使用
前言
目前公司升级系统,需要使用MVVM来展示复杂的页面,如详情等
这里说明 越是复杂的详情 越需要使用UITableView展示
主要是方便后期维护 我改过几个整个详情页面都适用xib画的 太可怕了
思路
1>详情页 肯定有一个UIViewController和Model
将model转化为需要显示的cell的数量
在Controller中实现UITableView的代理
进而展示数据
2>将model转化为cell的数量 这个步骤写在Controller中 会导致Controller 代码量很大 不好维护 可以创建一个对象完成这个步骤
3>每个页面都实现UITableView的代理,太复杂了 将UITableView的代理方法封装 方便使用 减少不必要的代码
4>对于tableview来说 如果在tabelview中写明具体使用某个cell 那封装将没有任何意义
这里是数组中保存的是model,所有的model都继承自baseModel 使用时从baseModel读取出cell 展示数据,将model传值到cell中
5>cell和controller的通信 展示数据好说 要是需要传值 怎么处理?
这里是使用block将数据或者值或者方法传到第2步创建好的对象中
在从对象中传到controller中
实现
1>封装tableview
需要一个 继承自NSObject 的UITableViewMiddle,主要是提供方法、实现代理、注册cell
UITableView分为section和row 这里就需要两个model UITableViewSectionModelBase 和 UITableViewCellModelBase 这两个model中都有基础参数 reuseIdentifier 和 rowHeight 用于确定是哪个cell和高度
使用时 从model中得到具体的cell名 在将model传入cell中,实现数据展示,这里可用KVC、RAC、FBKVOController等各种方法
下面说下具体的实现
1.创建UITableViewMiddle 继承自NSObject 主要是为了实现tableview的代理事件
.h中代码如下
@interface UITableViewMiddle : NSObject<UITableViewDataSource, UITableViewDelegate>
+ (instancetype)tableViewPersenterWithTableView:(UITableView *)tableView
arrSectionModels:(NSArray<__kindof UITableViewSectionModelBase *> *)arrSectionModels;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong, readonly) NSArray<__kindof UITableViewSectionModelBase *> *arrSectionModels;
- (void)updateSectionModels:(NSArray<__kindof UITableViewSectionModelBase *> *)arrSectionModels;
@end
可以看到里面有一个类方法,一个控件tableView,一个数组,一个更新方法
有以上这几个参数就足够了 其他的需要再补充
.m中的代码主要是分为两部分 其中之一是实现类方法 另一个是实现tableview的代理事件
这里重点说下 类方法的实现
众所周知 tableview代理中需要先注册cell ,我们使用的是统一的封装方法 所以注册cell这步骤需要在类方法中实现 这一步还需要考虑 是xib创建 还是纯代码创建
上代码
+ (instancetype)tableViewPersenterWithTableView:(UITableView *)tableView
arrSectionModels:(NSArray<__kindof UITableViewSectionModelBase *> *)arrSectionModels
{
UITableViewMiddle *persenter = [UITableViewMiddle new];
persenter.tableView = tableView;
persenter.arrSectionModels = arrSectionModels;
[persenter registerCellAndHeaderFooterViews];
return persenter;
}
- (void)updateSectionModels:(NSArray<__kindof UITableViewSectionModelBase *> *)arrSectionModels
{
self.arrSectionModels = arrSectionModels;
[self registerCellAndHeaderFooterViews];
[self.tableView reloadData];
}
- (void)registerCellAndHeaderFooterViews
{
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.arrSectionModels enumerateObjectsUsingBlock:^(__kindof UITableViewSectionModelBase * _Nonnull sectionModel, NSUInteger idx, BOOL * _Nonnull stop) {
if (sectionModel.headerViewModel.headerFooterReuseIdentifier) {
switch (sectionModel.headerViewModel.initMode) {
case ClassInitializationMode_Xib:
{
[self.tableView registerNib:[UINib nibWithNibName:sectionModel.headerViewModel.headerFooterReuseIdentifier bundle:[NSBundle mainBundle]] forHeaderFooterViewReuseIdentifier:sectionModel.headerViewModel.headerFooterReuseIdentifier];
}
break;
case ClassInitializationMode_Code:
{
[self.tableView registerClass:NSClassFromString(sectionModel.headerViewModel.headerFooterReuseIdentifier) forHeaderFooterViewReuseIdentifier:sectionModel.headerViewModel.headerFooterReuseIdentifier];
}
break;
}
}
if (sectionModel.footerViewModel.headerFooterReuseIdentifier) {
switch (sectionModel.footerViewModel.initMode) {
case ClassInitializationMode_Xib:
{
[self.tableView registerNib:[UINib nibWithNibName:sectionModel.footerViewModel.headerFooterReuseIdentifier bundle:[NSBundle mainBundle]] forHeaderFooterViewReuseIdentifier:sectionModel.footerViewModel.headerFooterReuseIdentifier];
}
break;
case ClassInitializationMode_Code:
{
[self.tableView registerClass:NSClassFromString(sectionModel.footerViewModel.headerFooterReuseIdentifier) forHeaderFooterViewReuseIdentifier:sectionModel.footerViewModel.headerFooterReuseIdentifier];
}
break;
}
}
[sectionModel.arrCellModels enumerateObjectsUsingBlock:^(UITableViewCellModelBase * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
switch (obj.initMode) {
case ClassInitializationMode_Xib:
{
[self.tableView registerNib:[UINib nibWithNibName:obj.reuseIdentifier
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:obj.reuseIdentifier];
}
break;
case ClassInitializationMode_Code:
{
[self.tableView registerClass:NSClassFromString(obj.reuseIdentifier)
forCellReuseIdentifier:obj.reuseIdentifier];
}
break;
}
}];
}];
}
2.UITableViewSectionModelBase 基础model 对应tableview的section
看下代码
@property (nonatomic, strong) UITableHeaderFooterViewModelBase *headerViewModel;
@property (nonatomic, strong) NSArray<__kindof UITableViewCellModelBase *> *arrCellModels;
@property (nonatomic, strong) UITableHeaderFooterViewModelBase *footerViewModel;
3.UITableHeaderFooterViewModelBase 对应tableview的headerView footerView
看下代码
@property (nonatomic, assign) BOOL isOpen;
@property (nonatomic, assign) NSInteger sectionIndex;
@property (nonatomic, assign) CGFloat headerFooterViewHeight;
@property (nonatomic, strong) NSString *headerFooterReuseIdentifier;
@property (nonatomic, assign) ClassInitializationMode initMode;
4.UITableViewCellModelBase 对象tableview的cell
看下代码
@property (nonatomic, strong) NSString * _Nonnull reuseIdentifier;
@property (nonatomic, assign) CGFloat estimatedRowHeight;
@property (nonatomic, assign) CGFloat rowHeight;
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, assign) ClassInitializationMode initMode;
/// 是否禁止选中,默认否
@property (nonatomic) BOOL isProhibitSelected;
/**
设置动态高度 rowHeight
*/
@property (nonatomic, copy, nullable) CGFloat (^ getCellHeightCallback)(void);
@property (nonatomic, copy, nullable) void (^ reloadTableCell)(void);
/// 刷新cell
- (void)reloadCell;
+ (instancetype _Nonnull )tableViewCellModelBaseWithReuseIdentifier:(NSString *_Nonnull)reuseIdentifier
estimatedRowHeight:(CGFloat)estimatedRowHeight
rowHeight:(CGFloat)rowHeight
index:(NSInteger)index;
@property (nonatomic, copy) void (^ _Nullable didSelectRowAtIndexPathCallback)(UITableView * _Nonnull tableView, NSIndexPath * _Nonnull indexPath);
这句封装好了整个tableview
整体目录如下
2>MVVMViewControllerObjc中功能完善
tableView代理中需要 UITableViewSectionModelBase
所以 我们最终是需要个数组 里面保存的都是UITableViewSectionModelBase对象
1.实现类方法
+ (instancetype)loadObjcWithController:(MVVMViewController *)controller
withTableView:(UITableView *)tableView
withDetailModel:(MVVMDetailModel *)detailModel
{
MVVMViewControllerObjc *objc = [[MVVMViewControllerObjc alloc]init];
objc.controller = controller;
objc.detailModel = detailModel;
objc.tableView = tableView;
[objc initializationData];
return objc;
}
2.实现方法initializationData 里面是添加对应的数据
- (void)initializationData
{
[self.arrSectionModels addObject:[self basicInformationSection]];
self.tableViewMiddle = [UITableViewMiddle tableViewPersenterWithTableView:self.tableView arrSectionModels:self.arrSectionModels];
[self.tableView reloadData];
}
- (UITableViewSectionModelBase *)basicInformationSection
{
UITableViewSectionModelBase *baseModel = [[UITableViewSectionModelBase alloc] init];
NSMutableArray *arrCellModels = [NSMutableArray array];
///名字
{
MVVMTestViewCellModel *cellModel = [MVVMTestViewCellModel tableViewCellModelWithEstimatedRowHeight:50 rowHeight:50 titleStr:self.detailModel.name];
[arrCellModels addObject:cellModel];
}
baseModel.arrCellModels = arrCellModels;
return baseModel;;
}
3>controller中调用
self.objc = [MVVMViewControllerObjc loadObjcWithController:self withTableView:self.tableView withDetailModel:self.detailModel];
这样是不是简单一点
ps:这是我同事写的,被我盗用了....
链接 https://pan.baidu.com/s/1fItpU-daMhUEjK0_2jeodg 提取码: mwn0