1、基于MVVM用于快速搭建设置页个人信息页的框架基于MVVM,用于快速搭建设置页,个人信息页的框架仿照了微信客户端的发现页,个人页和设置页写了一个Demo,来看一下效果图:先不要纠结分组定制和同组定制的具体意思,在后面讲到定制性的时候我会详细说明。现在只是让大家看一下效果。在大概了解了功能之后,开始详细介绍这个框架。写这篇介绍的原因倒不是希望有多少人来用,而是表达一下我自己的思路而已。各位觉得不好的地方请多批评。在正式讲解之前,先介绍一下本篇的基本目录: 1. 用到的技术点。 2. 功能说明。 3. 使用方法。 4. 定制性介绍。1. 用到的技术点框架整体来说还是比较简单的,主要还是基于苹果的U
2、ITableView组件,为了解耦和责任分离,主要运用了以下技术点: - MVVM:采用MVVM架构,将每一行“纯粹”的数据交给一个单独的ViewModel,让其持有每个cell的数据(行高,cell类型,文本宽度,图片高度等等)。而且每一个section也对应一个ViewModel,它持有当前section的配置数据(title,header和footer的高度等等)。 - 轻UIViewController:分离UITableViewDataSource与UIViewController,让单独一个类来实现UITableViewDataSource的职能。 - block:使用block来
3、调用cell的绘制方法。 - 分类:使用分类来定义每一种不同的cell的绘制方法。知道了主要运用的技术点以后,给大家详细介绍一下该框架的功能。2. 功能介绍这个框架可以用来快速搭建设置页,个人信息页能静态表格页面,使用者只需要给tableView的DataSource传入元素是viewModel的数组就可以了。虽说这类页面的布局还是比较单一的,但是还是会有几种不同的情况(cell的布局类型),我对比较常见的cell布局做了封装,使用者可以直接使用。我在定义这些cell的类型的时候,大致划分了两类: 1. 第一类是系统风格的cell,大多数情况下,cell高度为44;在cell左侧会有一张图,一
4、个label,也可以只存在一种(但是只存在图片的情况很少);在cell右侧一般都有一个向右的箭头,而且有时这个箭头的左侧还可能有label,image,也可以两个都有。 2. 第二类就是自定义的cell了,它的高度不一定是44,而且布局和系统风格的cell很不一样,需要用户自己添加。基于这两大类,再细分了几种情况,可以由下面这张图来直观看一下:既然是cell的类型,那么就类型的枚举就需要定义在cell的viewModel里面:typedef NS_ENUM(NSInteger, SJStaticCellType) /系统风格的各种cell类型,已封装好,可以直接用 SJStaticCellTy
5、peSystemLogout, /退出登录cell SJStaticCellTypeSystemAccessoryNone, /右侧没有任何控件 SJStaticCellTypeSystemAccessorySwitch, /右侧是开关 SJStaticCellTypeSystemAccessoryDisclosureIndicator, /右侧是三角箭头(箭头左侧可以有一个image或者一个label,或者二者都有,根据传入的参数决定) /需要用户自己添加的自定义cell类型 SJStaticCellTypeMeAvatar, /个人页“我”cell ;在这里有三点需要说一下:这里面除了自定
6、义的cell以外,其他类型的cell都不需要开发者自己布局,都已经被我封装好,只需要在cell的ViewModel里面传入相应的类型和数据(文字,图片)即可。因为左侧的两个控件(图片和文字)是至少存在一个而且左右顺序固定(图片永远在最左侧),所以该框架通过开发者传入的左侧需要显示的图片和文字,可以自己进行cell的布局。所以类型的判断主要作用于cell的右侧。值得一提的是,在”最右侧是一个箭头”子分支的五个类型其实都属于一个类型,只需要传入文字和图片,以及文字图片的显示顺序参数(这个参数只在同时存在图片和文字的时候有效)就可以自行判断布局。在了解了该框架的功能之后,我们先看一下如何使用这个框架
7、:3. 使用方法最开始先用文字说明一下:将SJStaticTableViewComponent文件夹复制到工程里。将要开发的页面的ViewController继承SJStaticTableViewController。在新ViewController里实现createDataSource方法,将viewModel数组传给控制器的dataSource属性。根据不同的cell类型,调用不同的cell绘制方法。如果需要接受cell的点击,需要实现didSelectViewModel方法。可能感觉比较抽象,我拿设置页来具体说明一下:先看一下设置页的布局:然后我们看一下设置的ViewController
8、的代码:- (void)viewDidLoad super viewDidLoad; self.navigationItem.title = 设置;- (void)createDataSource self.dataSource = SJStaticTableViewDataSource alloc initWithViewModelsArray:Factory settingPageData configureBlock:(SJStaticTableViewCell *cell, SJStaticTableviewCellViewModel *viewModel) switch (viewM
9、odel.staticCellType) case SJStaticCellTypeSystemAccessoryDisclosureIndicator: cell configureAccessoryDisclosureIndicatorCellWithViewModel:viewModel; break; case SJStaticCellTypeSystemAccessorySwitch: cell configureAccessorySwitchCellWithViewModel:viewModel; break; case SJStaticCellTypeSystemLogout:
10、cell configureLogoutTableViewCellWithViewModel:viewModel; break; case SJStaticCellTypeSystemAccessoryNone: cell configureAccessoryNoneCellWithViewModel:viewModel; break; default: break; ;- (void)didSelectViewModel:(SJStaticTableviewCellViewModel *)viewModel atIndexPath:(NSIndexPath *)indexPath switc
11、h (viewModel.identifier) case 6: NSLog(退出登录); self showAlertWithMessage:真的要退出登录嘛?; break; case 8: NSLog(清理缓存); break; case 9: NSLog(跳转到定制性cell展示页面 - 分组); SJCustomCellsViewController *vc = SJCustomCellsViewController alloc init; self.navigationController pushViewController:vc animated:YES; break; cas
12、e 10: NSLog(跳转到定制性cell展示页面 - 同组); SJCustomCellsOneSectionViewController *vc = SJCustomCellsOneSectionViewController alloc init; self.navigationController pushViewController:vc animated:YES; break; default: break; 看到这里,你可能会有这些疑问: 1. UITableViewDataSource方法哪儿去了? 2. viewModel数组是如何设置的? 3. cell的绘制方法是如何区分
13、的? 4. UITableViewDelegate的方法哪里去了?下面我会一一解答,看完了下面的解答,就能几乎完全掌握这个框架的思路了:问题1:UITableViewDataSource方法哪儿去了?我自己封装了一个类SJStaticTableViewDataSource专门作为数据源,需要控制器给它一个viewModel数组。来看一下它的实现文件:/SJStaticTableViewDataSource.m- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView return self.viewModelsArray.
14、count;- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section SJStaticTableviewSectionViewModel *vm = self.viewModelsArraysection; return vm.cellViewModelsArray.count;- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPa
15、th /获取section的ViewModel SJStaticTableviewSectionViewModel *sectionViewModel = self.viewModelsArrayindexPath.section; /获取cell的viewModel SJStaticTableviewCellViewModel *cellViewModel = sectionViewModel.cellViewModelsArrayindexPath.row; SJStaticTableViewCell *cell = tableView dequeueReusableCellWithIde
16、ntifier:cellViewModel.cellID; if (!cell) cell = SJStaticTableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellViewModel.cellID; self.cellConfigureBlock(cell,cellViewModel); return cell;- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)sec
17、tion SJStaticTableviewSectionViewModel *vm = self.viewModelsArraysection; return vm.sectionHeaderTitle; - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section SJStaticTableviewSectionViewModel *vm = self.viewModelsArraysection; return vm.sectionFooterTitle;表格的cel
18、l和section都设置了与其对应的viewModel,用于封装其对应的数据:cell的viewModel(大致看一下即可,后面有详细说明):typedef NS_ENUM(NSInteger, SJStaticCellType) /系统风格的各种cell类型,已封装好,可以直接用 SJStaticCellTypeSystemLogout, /退出登录cell(已封装好) SJStaticCellTypeSystemAccessoryNone, /右侧没有任何控件 SJStaticCellTypeSystemAccessorySwitch, /右侧是开关 SJStaticCellTypeSys
19、temAccessoryDisclosureIndicator, /右侧是三角箭头(箭头左侧可以有一个image或者一个label,或者二者都有,根据传入的参数决定) /需要用户自己添加的自定义cell类型 SJStaticCellTypeMeAvatar, /个人页“我”cell;typedef void(SwitchValueChagedBlock)(BOOL isOn); /switch开关切换时调用的blockinterface SJStaticTableviewCellViewModel : NSObjectproperty (nonatomic, assign) SJStaticC
20、ellType staticCellType; /类型property (nonatomic, copy) NSString *cellID; /cell reuser identifierproperty (nonatomic, assign) NSInteger identifier; /区别每个cell,用于点击/ = 系统默认cell左侧 = /property (nonatomic, strong) UIImage *leftImage; /左侧的image,按需传入property (nonatomic, assign) CGSize leftImageSize; /左侧image
21、的大小,存在默认设置property (nonatomic, copy) NSString *leftTitle; /cell主标题,按需传入property (nonatomic, strong) UIColor *leftLabelTextColor; /当前组cell左侧label里文字的颜色property (nonatomic, strong) UIFont *leftLabelTextFont; /当前组cell左侧label里文字的字体property (nonatomic, assign) CGFloat leftImageAndLabelGap; /左侧image和label
22、的距离,存在默认值/ = 系统默认cell右侧 = /property (nonatomic, copy) NSString *indicatorLeftTitle; /右侧箭头左侧的文本,按需传入property (nonatomic, strong) UIColor *indicatorLeftLabelTextColor; /右侧文字的颜色,存在默认设置,也可以自定义property (nonatomic, strong) UIFont *indicatorLeftLabelTextFont; /右侧文字的字体,存在默认设置,也可以自定义property (nonatomic, stro
23、ng) UIImage *indicatorLeftImage; /右侧箭头左侧的image,按需传入property (nonatomic, assign) CGSize indicatorLeftImageSize; /右侧尖头左侧image大小,存在默认设置,也可以自定义property (nonatomic, assign, readonly) BOOL hasIndicatorImageAndLabel; /右侧尖头左侧的文本和image是否同时存在,只能通过内部计算property (nonatomic, assign) CGFloat indicatorLeftImageAndL
24、abelGap; /右侧尖头左侧image和label的距离,存在默认值property (nonatomic, assign) BOOL isImageFirst; /右侧尖头左侧的文本和image同时存在时,是否是image挨着箭头,默认为YESproperty (nonatomic, copy) SwitchValueChagedBlock switchValueDidChangeBlock; /切换switch开关的时候调用的block/ = 长宽数据 = /property (nonatomic, assign) CGFloat cellHeight; /cell高度,默认是44,可
25、以设置property (nonatomic, assign) CGSize leftTitleLabelSize; /左侧默认Label的size,传入text以后内部计算property (nonatomic, assign) CGSize indicatorLeftLabelSize; /右侧label的size/ = 自定义cell的数据放在这里 = /property (nonatomic, strong) UIImage *avatarImage;property (nonatomic, strong) UIImage *codeImage;property (nonatomic,
26、 copy) NSString *userName;property (nonatomic, copy) NSString *userID;section的viewModel(大致看一下即可,后面有详细说明):interface SJStaticTableviewSectionViewModel : NSObjectproperty (nonatomic, copy) NSString *sectionHeaderTitle; /该section的标题property (nonatomic, copy) NSString *sectionFooterTitle; /该section的标题pro
27、perty (nonatomic, strong) NSArray *cellViewModelsArray; /该section的数据源property (nonatomic, assign) CGFloat sectionHeaderHeight; /header的高度property (nonatomic, assign) CGFloat sectionFooterHeight; /footer的高度property (nonatomic, assign) CGSize leftImageSize; /当前组cell左侧image的大小property (nonatomic, stron
28、g) UIColor *leftLabelTextColor; /当前组cell左侧label里文字的颜色property (nonatomic, strong) UIFont *leftLabelTextFont; /当前组cell左侧label里文字的字体property (nonatomic, assign) CGFloat leftImageAndLabelGap; /当前组左侧image和label的距离,存在默认值property (nonatomic, strong) UIColor *indicatorLeftLabelTextColor; /当前组cell右侧label里文字
29、的颜色property (nonatomic, strong) UIFont *indicatorLeftLabelTextFont; /当前组cell右侧label里文字的字体property (nonatomic, assign) CGSize indicatorLeftImageSize; /当前组cell右侧image的大小property (nonatomic, assign) CGFloat indicatorLeftImageAndLabelGap;/当前组cell右侧image和label的距离,存在默认值- (instancetype)initWithCellViewMode
30、lsArray:(NSArray *)cellViewModelsArray;你可能会觉得属性太多了,但这些属性的存在意义是为cell的定制性服务的,在后文会有解释。现在了解了我封装好的数据源,cell的viewModel,section的viewModel以后,我们看一下第二个问题:问题2: viewModel数组是如何设置的?我们来看一下设置页的viewModel数组的设置:+ (NSArray *)settingPageData / = section 0 SJStaticTableviewCellViewModel *vm0 = SJStaticTableviewCellViewModel alloc init; vm0.leftTitle = 账号与安全; vm0.identifier = 0; vm0.indicatorLeftTitle = 已保护; vm0.indicatorLeftImage = UIImage imageNamed:ProfileLockOn; vm0.isImageFirst =
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1