基于MVVM用于快速搭建设置页个人信息页的框架.docx
- 文档编号:29028570
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:33
- 大小:327.20KB
基于MVVM用于快速搭建设置页个人信息页的框架.docx
《基于MVVM用于快速搭建设置页个人信息页的框架.docx》由会员分享,可在线阅读,更多相关《基于MVVM用于快速搭建设置页个人信息页的框架.docx(33页珍藏版)》请在冰豆网上搜索。
基于MVVM用于快速搭建设置页个人信息页的框架
基于MVVM,用于快速搭建设置页,个人信息页的框架
仿照了微信客户端的发现页,个人页和设置页写了一个Demo,来看一下效果图:
先不要纠结分组定制和同组定制的具体意思,在后面讲到定制性的时候我会详细说明。
现在只是让大家看一下效果。
在大概了解了功能之后,开始详细介绍这个框架。
写这篇介绍的原因倒不是希望有多少人来用,而是表达一下我自己的思路而已。
各位觉得不好的地方请多批评。
在正式讲解之前,先介绍一下本篇的基本目录:
1.用到的技术点。
2.功能说明。
3.使用方法。
4.定制性介绍。
1.用到的技术点
框架整体来说还是比较简单的,主要还是基于苹果的UITableView组件,为了解耦和责任分离,主要运用了以下技术点:
-MVVM:
采用MVVM架构,将每一行“纯粹”的数据交给一个单独的ViewModel,让其持有每个cell的数据(行高,cell类型,文本宽度,图片高度等等)。
而且每一个section也对应一个ViewModel,它持有当前section的配置数据(title,header和footer的高度等等)。
-轻UIViewController:
分离UITableViewDataSource与UIViewController,让单独一个类来实现UITableViewDataSource的职能。
-block:
使用block来调用cell的绘制方法。
-分类:
使用分类来定义每一种不同的cell的绘制方法。
知道了主要运用的技术点以后,给大家详细介绍一下该框架的功能。
2.功能介绍
这个框架可以用来快速搭建设置页,个人信息页能静态表格页面,使用者只需要给tableView的DataSource传入元素是viewModel的数组就可以了。
虽说这类页面的布局还是比较单一的,但是还是会有几种不同的情况(cell的布局类型),我对比较常见的cell布局做了封装,使用者可以直接使用。
我在定义这些cell的类型的时候,大致划分了两类:
1.第一类是系统风格的cell,大多数情况下,cell高度为44;在cell左侧会有一张图,一个label,也可以只存在一种(但是只存在图片的情况很少);在cell右侧一般都有一个向右的箭头,而且有时这个箭头的左侧还可能有label,image,也可以两个都有。
2.第二类就是自定义的cell了,它的高度不一定是44,而且布局和系统风格的cell很不一样,需要用户自己添加。
基于这两大类,再细分了几种情况,可以由下面这张图来直观看一下:
既然是cell的类型,那么就类型的枚举就需要定义在cell的viewModel里面:
typedefNS_ENUM(NSInteger,SJStaticCellType){
//系统风格的各种cell类型,已封装好,可以直接用
SJStaticCellTypeSystemLogout,//退出登录cell
SJStaticCellTypeSystemAccessoryNone,//右侧没有任何控件
SJStaticCellTypeSystemAccessorySwitch,//右侧是开关
SJStaticCellTypeSystemAccessoryDisclosureIndicator,//右侧是三角箭头(箭头左侧可以有一个image或者一个label,或者二者都有,根据传入的参数决定)
//需要用户自己添加的自定义cell类型
SJStaticCellTypeMeAvatar,//个人页“我”cell
};
在这里有三点需要说一下:
这里面除了自定义的cell以外,其他类型的cell都不需要开发者自己布局,都已经被我封装好,只需要在cell的ViewModel里面传入相应的类型和数据(文字,图片)即可。
因为左侧的两个控件(图片和文字)是至少存在一个而且左右顺序固定(图片永远在最左侧),所以该框架通过开发者传入的左侧需要显示的图片和文字,可以自己进行cell的布局。
所以类型的判断主要作用于cell的右侧。
值得一提的是,在”最右侧是一个箭头”子分支的五个类型其实都属于一个类型,只需要传入文字和图片,以及文字图片的显示顺序参数(这个参数只在同时存在图片和文字的时候有效)就可以自行判断布局。
在了解了该框架的功能之后,我们先看一下如何使用这个框架:
3.使用方法
最开始先用文字说明一下:
将SJStaticTableViewComponent文件夹复制到工程里。
将要开发的页面的ViewController继承SJStaticTableViewController。
在新ViewController里实现createDataSource方法,将viewModel数组传给控制器的dataSource属性。
根据不同的cell类型,调用不同的cell绘制方法。
如果需要接受cell的点击,需要实现didSelectViewModel方法。
可能感觉比较抽象,我拿设置页来具体说明一下:
先看一下设置页的布局:
然后我们看一下设置的ViewController的代码:
-(void)viewDidLoad{
[superviewDidLoad];
self.navigationItem.title=@"设置";
}
-(void)createDataSource
{
self.dataSource=[[SJStaticTableViewDataSourcealloc]initWithViewModelsArray:
[FactorysettingPageData]configureBlock:
^(SJStaticTableViewCell*cell,SJStaticTableviewCellViewModel*viewModel){
switch(viewModel.staticCellType)
{
caseSJStaticCellTypeSystemAccessoryDisclosureIndicator:
{
[cellconfigureAccessoryDisclosureIndicatorCellWithViewModel:
viewModel];
}
break;
caseSJStaticCellTypeSystemAccessorySwitch:
{
[cellconfigureAccessorySwitchCellWithViewModel:
viewModel];
}
break;
caseSJStaticCellTypeSystemLogout:
{
[cellconfigureLogoutTableViewCellWithViewModel:
viewModel];
}
break;
caseSJStaticCellTypeSystemAccessoryNone:
{
[cellconfigureAccessoryNoneCellWithViewModel:
viewModel];
}
break;
default:
break;
}
}];
}
-(void)didSelectViewModel:
(SJStaticTableviewCellViewModel*)viewModelatIndexPath:
(NSIndexPath*)indexPath
{
switch(viewModel.identifier)
{
case6:
{
NSLog(@"退出登录");
[selfshowAlertWithMessage:
@"真的要退出登录嘛?
"];
}
break;
case8:
{
NSLog(@"清理缓存");
}
break;
case9:
{
NSLog(@"跳转到定制性cell展示页面-分组");
SJCustomCellsViewController*vc=[[SJCustomCellsViewControlleralloc]init];
[self.navigationControllerpushViewController:
vcanimated:
YES];
}
break;
case10:
{
NSLog(@"跳转到定制性cell展示页面-同组");
SJCustomCellsOneSectionViewController*vc=[[SJCustomCellsOneSectionViewControlleralloc]init];
[self.navigationControllerpushViewController:
vcanimated:
YES];
}
break;
default:
break;
}
}
看到这里,你可能会有这些疑问:
1.UITableViewDataSource方法哪儿去了?
2.viewModel数组是如何设置的?
3.cell的绘制方法是如何区分的?
4.UITableViewDelegate的方法哪里去了?
下面我会一一解答,看完了下面的解答,就能几乎完全掌握这个框架的思路了:
问题1:
UITableViewDataSource方法哪儿去了?
我自己封装了一个类SJStaticTableViewDataSource专门作为数据源,需要控制器给它一个viewModel数组。
来看一下它的实现文件:
//SJStaticTableViewDataSource.m
-(NSInteger)numberOfSectionsInTableView:
(UITableView*)tableView
{
returnself.viewModelsArray.count;
}
-(NSInteger)tableView:
(UITableView*)tableViewnumberOfRowsInSection:
(NSInteger)section
{
SJStaticTableviewSectionViewModel*vm=self.viewModelsArray[section];
returnvm.cellViewModelsArray.count;
}
-(UITableViewCell*)tableView:
(UITableView*)tableViewcellForRowAtIndexPath:
(NSIndexPath*)indexPath
{
//获取section的ViewModel
SJStaticTableviewSectionViewModel*sectionViewModel=self.viewModelsArray[indexPath.section];
//获取cell的viewModel
SJStaticTableviewCellViewModel*cellViewModel=sectionViewModel.cellViewModelsArray[indexPath.row];
SJStaticTableViewCell*cell=[tableViewdequeueReusableCellWithIdentifier:
cellViewModel.cellID];
if(!
cell){
cell=[[SJStaticTableViewCellalloc]initWithStyle:
UITableViewCellStyleDefaultreuseIdentifier:
cellViewModel.cellID];
}
self.cellConfigureBlock(cell,cellViewModel);
returncell;
}
-(NSString*)tableView:
(UITableView*)tableViewtitleForHeaderInSection:
(NSInteger)section{
SJStaticTableviewSectionViewModel*vm=self.viewModelsArray[section];
returnvm.sectionHeaderTitle;
}
-(NSString*)tableView:
(UITableView*)tableViewtitleForFooterInSection:
(NSInteger)section
{
SJStaticTableviewSectionViewModel*vm=self.viewModelsArray[section];
returnvm.sectionFooterTitle;
}
表格的cell和section都设置了与其对应的viewModel,用于封装其对应的数据:
cell的viewModel(大致看一下即可,后面有详细说明):
typedefNS_ENUM(NSInteger,SJStaticCellType){
//系统风格的各种cell类型,已封装好,可以直接用
SJStaticCellTypeSystemLogout,//退出登录cell(已封装好)
SJStaticCellTypeSystemAccessoryNone,//右侧没有任何控件
SJStaticCellTypeSystemAccessorySwitch,//右侧是开关
SJStaticCellTypeSystemAccessoryDisclosureIndicator,//右侧是三角箭头(箭头左侧可以有一个image或者一个label,或者二者都有,根据传入的参数决定)
//需要用户自己添加的自定义cell类型
SJStaticCellTypeMeAvatar,//个人页“我”cell
};
typedefvoid(^SwitchValueChagedBlock)(BOOLisOn);//switch开关切换时调用的block
@interfaceSJStaticTableviewCellViewModel:
NSObject
@property(nonatomic,assign)SJStaticCellTypestaticCellType;//类型
@property(nonatomic,copy)NSString*cellID;//cellreuseridentifier
@property(nonatomic,assign)NSIntegeridentifier;//区别每个cell,用于点击
//===============系统默认cell左侧===============//
@property(nonatomic,strong)UIImage*leftImage;//左侧的image,按需传入
@property(nonatomic,assign)CGSizeleftImageSize;//左侧image的大小,存在默认设置
@property(nonatomic,copy)NSString*leftTitle;//cell主标题,按需传入
@property(nonatomic,strong)UIColor*leftLabelTextColor;//当前组cell左侧label里文字的颜色
@property(nonatomic,strong)UIFont*leftLabelTextFont;//当前组cell左侧label里文字的字体
@property(nonatomic,assign)CGFloatleftImageAndLabelGap;//左侧image和label的距离,存在默认值
//===============系统默认cell右侧===============//
@property(nonatomic,copy)NSString*indicatorLeftTitle;//右侧箭头左侧的文本,按需传入
@property(nonatomic,strong)UIColor*indicatorLeftLabelTextColor;//右侧文字的颜色,存在默认设置,也可以自定义
@property(nonatomic,strong)UIFont*indicatorLeftLabelTextFont;//右侧文字的字体,存在默认设置,也可以自定义
@property(nonatomic,strong)UIImage*indicatorLeftImage;//右侧箭头左侧的image,按需传入
@property(nonatomic,assign)CGSizeindicatorLeftImageSize;//右侧尖头左侧image大小,存在默认设置,也可以自定义
@property(nonatomic,assign,readonly)BOOLhasIndicatorImageAndLabel;//右侧尖头左侧的文本和image是否同时存在,只能通过内部计算
@property(nonatomic,assign)CGFloatindicatorLeftImageAndLabelGap;//右侧尖头左侧image和label的距离,存在默认值
@property(nonatomic,assign)BOOLisImageFirst;//右侧尖头左侧的文本和image同时存在时,是否是image挨着箭头,默认为YES
@property(nonatomic,copy)SwitchValueChagedBlockswitchValueDidChangeBlock;//切换switch开关的时候调用的block
//===============长宽数据===============//
@property(nonatomic,assign)CGFloatcellHeight;//cell高度,默认是44,可以设置
@property(nonatomic,assign)CGSizeleftTitleLabelSize;//左侧默认Label的size,传入text以后内部计算
@property(nonatomic,assign)CGSizeindicatorLeftLabelSize;//右侧label的size
//===============自定义cell的数据放在这里===============//
@property(nonatomic,strong)UIImage*avatarImage;
@property(nonatomic,strong)UIImage*codeImage;
@property(nonatomic,copy)NSString*userName;
@property(nonatomic,copy)NSString*userID;
section的viewModel(大致看一下即可,后面有详细说明):
@interfaceSJStaticTableviewSectionViewModel:
NSObject
@property(nonatomic,copy)NSString*sectionHeaderTitle;//该section的标题
@property(nonatomic,copy)NSString*sectionFooterTitle;//该section的标题
@property(nonatomic,strong)NSArray*cellViewModelsArray;//该section的数据源
@property(nonatomic,assign)CGFloatsectionHeaderHeight;//header的高度
@property(nonatomic,assign)CGFloatsectionFooterHeight;//footer的高度
@property(nonatomic,assign)CGSizeleftImageSize;//当前组cell左侧image的大小
@property(nonatomic,strong)UIColor*leftLabelTextColor;//当前组cell左侧label里文字的颜色
@property(nonatomic,strong)UIFont*leftLabelTextFont;//当前组cell左侧label里文字的字体
@property(nonatomic,assign)CGFloatleftImageAndLabelGap;//当前组左侧image和label的距离,存在默认值
@property(nonatomic,strong)UIColor*indicatorLeftLabelTextColor;//当前组cell右侧label里文字的颜色
@property(nonatomic,strong)UIFont*indicatorLeftLabelTextFont;//当前组cell右侧label里文字的字体
@property(nonatomic,assign)CGSizeindicatorLeftImageSize;//当前组cell右侧image的大小
@property(nonatomic,assign)CGFloatindicatorLeftImageAndLabelGap;//当前组cell右侧image和label的距离,存在默认值
-(instancetype)initWithCellViewModelsArray:
(NSArray*)cellViewModelsArray;
你可能会觉得属性太多了,但这些属性的存在意义是为cell的定制性服务的,在后文会有解释。
现在了解了我封装好的数据源,cell的viewModel,section的viewModel以后,我们看一下第二个问题:
问题2:
viewModel数组是如何设置的?
我们来看一下设置页的viewModel数组的设置:
+(NSArray*)settingPageData
{
//==========section0
SJStaticTableviewCellViewModel*vm0=[[SJStaticTableviewCellViewModelalloc]init];
vm0.leftTitle=@"账号与安全";
vm0.identifier=0;
vm0.indicatorLeftTitle=@"已保护";
vm0.indicatorLeftImage=[UIImageimageNamed:
@"ProfileLockOn"];
vm0.isImageFirst=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 MVVM 用于 快速 搭建 设置 个人信息 框架
![提示](https://static.bdocx.com/images/bang_tan.gif)