严肃的目录
一、前言
1.写之前的废话
二、MVVM和RAC理解
1.MVVM浅谈
2.RAC浅谈
3.两者的结合运用
三、架构部分
1.演示架构详解
2.基类说明
3.对上述吧啦吧啦几句
四、实战部分
1.ViewController的处理
2.View的处理
3.Model的处理
4.ViewModel的处理
五、后记
1.程序员话痨模式开启
六、基情感谢
1.程序员之间的满满基情
一、写之前吧啦吧啦几句
经过第一篇文章的多愁善感,接下来是表演真正的绝技的时候了,回归我的老本行,宇宙无敌iOS软件开发工程师!(装逼完毕,此处可以有点掌声)最近的IT行情不太好了,很多小狮子们都找不到工作了,面试的时候,也会经常被问到MVC等设计模式和架构,今天我就谈谈自己的理解,当然了,我也是刚从小白变成小白发点黑,刚接手的公司的项目用到的是MVVM+RAC的框架,现在终于有那么点滴的了解了,希望能帮助你那么一点,就是我的初衷了。设计模式这东西,说简单也难,说难也不难,嘎嘎嘎,总之万变不离其宗,用到MVVM我们的目标就是,让VC这个胖子减减肥,代码看起来颜值高点,维护的时候方便点,数据处理更清晰点,耦合度低一点,可复用性高点,独立开发更简单点.....也就是那么一点优点啦。废话不多说,开始ing。
二、MVVM和RAC理解
1.MVVM浅谈
MVC是构建iOS App的标准模式,是苹果推荐的一个用来组织代码的权威范式,市面上大部分App都是这样构建的,具体组建模式不细说,iOS入门者都比较了解(虽然不一定能完全去遵守),但其几个不能避免的问题却是很严重困扰开发者比如厚重的ViewController、遗失的网络逻辑(没有属于它的位置)、较差的可测试性等因此也就会有维护性较强、耦合性很低的一种新架构MVVM (MVC 引申出得新的架构)的流行。
MVVM虽然来自微软,但是不应该反对它,它正式规范了正式规范了视图和控制器紧耦合的性质,如下图:
写到这里,我看见下面有同学🙋说,ViewModel是什么鬼?
View绑定到ViewModel,然后执行一些命令在向它请求一个动作。而反过来,ViewModel跟Model通讯,告诉它更新来响应UI。这样便使得应用构建UI非常的容易。相比较于MVC新引入的视图模型。是视图显示逻辑、验证逻辑、网络请求等代码存放的地方,唯一要注意的是,任何视图本身的引用都不应该放在VM中,换句话说就是VM中不要引入UIKit.h。
这样,首先解决了VC臃肿的问题,将逻辑代码、网络请求等都写入了VM中,然后又由于VM中包含了所有的展示逻辑而且不会引用V,所以它是可以通过编程充分测试的。
2.RAC浅谈
ReactiveCocoa是响应式编程(FRP)在iOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# ;在网上看了几篇文章,感觉理论讲了很多,但是代码还是看不太懂,于是自己把它github文档上的一些使用的经典示例实现了一下,项目中有需要时可以直接搬过去用,用的熟练了再读源码也比较容易理解。
RAC虽然最大的优点是提供了一个单一的、统一的方法去处理异步的行为,包括delegate方法,blocks回调,target-action机制,notifications和KVO.但是不要简单的只是单纯的认为他仅仅就是减少代码复杂度,更好的配合MVVM而已,小伙子,这样你就小看它了。
它最大的与众不同是提供了一种新的写代码的思维,**由于RAC将Cocoa中KVO、UIKit event、delegate、selector等都增加了RAC支持,所以都不用去做很多跨函数的事。
这里我就不细说了,里面的东西还是很多滴,需要慢慢沉淀,这里我推荐一篇写的比较好的教程,大家互相学习。http://www.jianshu.com/p/87ef6720a096
3.两者结合的应用
在此次介绍中,会使用MVVM+RAC结合的方式,搞定一个添加上拉加载及下拉刷新的列表,所以更多的诠释MVVM思想,而不是RAC的逻辑链式操作(这一点用登录界面来写更能体现YoY ),RAC在此扮演的更大一部分的角色是更好的解耦,减少代码复杂度,使代码层次分明、逻辑清晰更便于维护升级。
二、架构部分
1.演示架构详解
废话不说半句多,原谅我这么不爱说话,啊啊啊啊,看图!
Frameworks
存放系统库的文件夹,新建工程的时候自己创建一个这个名字的文件夹,添加的系统库就会乖乖进来啦😄
AppDelegate
谁不知道这是干嘛的,你过来,我保证请你吃肉
Cl0ass
工程的主体,日常代码就放到这里了
General : 通用类(文件夹项目移植过程中都不需要更改的就能直接使用的)Base : 基类 (整个框架的基类)
Categories : 公共扩展类 (就是一些常用的类别,比如分享啊什么的)
Core : 公共核心类(一般存放个人信息、接口API等)
Models : 公共Model (公用的一些数据模型)
Views : 公共View (封装的一些常用的View)
Helpers : 工程的相关辅助类(比如类似数据请求、表单上传、网络监测等工具类)
** Macro :** 宏定义类
** Sections :** 各模块的文件夹(一般而言,我们以人为单位,毕竟项目很多时候是多人开发,每个人创建一个Sections互不影响,里面就存放自己的所有代码和页面啦)
Vendors : 第三方的类库/SDK,如UMeng、WeiboSDK、WeixinSDK等等。
Resource
这里放置的是工程所需的一些资源,如下
Fonts 字体
Images 图片
Sounds 声音
Videos 视频
Pods
当你开发iOS应用时,会经常使用到很多第三方开源类库,比如JSONKit,AFNetWorking等等。可能某个类库又用到其他类库,所以要使用它,必须得另外下载其他类库,而其他类库又用到其他类库,“子子孙孙无穷尽也”,反正在早期我是体会过这种痛苦,好心酸,手动一个个去下载所需类库是十分麻烦的。
还有另外一种常见情况是,你项目中用到的类库有更新,你必须得重新下载新版本,重新加入到项目中,十分麻烦。
CocoaPods就是帮你解决上面的问题的,话说这玩意应该是iOS最常用最有名的类库管理工具了,作为iOS程序员的我们,掌握CocoaPods的使用是必不可少的基本技能了,至于这玩意该咋用?
嘎嘎嘎,如果你看的足够细心,给你们点福利,这个网站可以学习哈,哎,我心软 http://code4app.com/article/cocoapods-install-usage
2.基类详解
这里着重讲解一下VC,View和ViewModel的基类。
1.YDViewController
yd_addSubviews : 添加View到ViewController
yd_bindViewModel : 用来绑定V(VC)与VM
yd_layoutNavigation : 设置导航栏、分栏
yd_getNewData : 初次获取数据的时候调用(不是特别必要)
2.YDView
yd_setupViews : 添加子View到主View
yd_bindViewModel : 绑定V与VM
yd_addReturnKeyBoard : 设置点击空白键盘回收
3.YDViewModel
yc_initialize : 进行一些逻辑绑定,网络数据请求处理。
LSRefreshDataStatus 数据处理后需要进行的操作标识LSHeaderRefresh_HasMoreData 下拉还有更多数据
LSHeaderRefresh_HasNoMoreData 下拉没有更多数据
LSFooterRefresh_HasMoreData 上拉还有更多数据
LSFooterRefresh_HasNoMoreData 上拉没有更多数据
LSRefreshError 刷新出错
LSRefreshUI 仅仅刷新UI布局
3.对上述吧啦吧啦几句
基类的作用是统一管理,统一风格,便于编码,有更多的额外的附加功能的话,建议使用Protocol 或 Category,这样移植性强,便于管理与扩展,不至于牵一发而动全身。
本篇基类核心是用VM来配置V(VC),并提供一些必须的Protocol方法来处理界面显示、逻辑,将代码风格规范化,各个部分的功能明朗化,这样,当你需要写什么,需要找什么,需要更改什么的时候都会很明确这些代码的位置,逻辑更清晰,而不会浪费更多的时间在思考应该写在哪,该去哪找,要改的地方在哪这种不该费时间的问题上。
四、实战部分
接下来,实现一个最基本的列表,由于本人比较懒,用我小伙伴的demo讲解下。话不多说,半句多,嘎嘎嘎,啦啦啊。
然后处理完后的目录如下:
简单介绍一下:
ViewController****LSCircleListViewController : 界面主控制器,负责跳转、Navgation、TabBar等
View****LSCircleListView : 界面主View,负责主要界面的显示
LSCircleListHeaderView : 头部Header,封装的内部含有一个CollectionView
LSCircleListCollectionCell : 头部Header中的CollectionView自定义的Cell
LSCircleListSectionHeaderView : SectionView,此界面不需复用,所以单纯一个View即可,若需要复用需要TableViewHeaderFooterView
LSCircleListTableCell : 主TableView的Cell
ViewModel****LSCircleListViewModel : 界面主ViewModel
LSCircleListHeaderViewModel : 头部Header对应的ViewModel
LSCircleListCollectionCellViewModel : 头部CollectionCell及TableViewCell的ViewModel(因为二者的数据结构是一致的)
LSCircleListSectionHeaderViewModel : Section的ViewModel
Model****LSCircleListModel : 圈子的数据模型(header和tableViewCell数据结构是一致的)
一个小小的界面这么多类...是不是难以接受了,淡定些,骚年!你要想想把这些个东西都放在VC内是个什么赶脚?也得好几千行呢!(有点夸张!不过也够头疼的),这么多类,这里着重讲一下主VC、主V、主VM、主M就ok,能详细讲明白MVVM之间是如何工作的就一通百通了。
1、LSCircleListViewController的处理
//
// LSCircleListViewController.m
// ZhongShui
//
// Created by 王隆帅 on 16/3/10.
// Copyright © 2016年 王隆帅. All rights reserved.
//
#import "LSCircleListViewController.h"
#import "LSCircleListView.h"
#import "LSCircleListViewModel.h"
#import "LSCircleMainPageViewController.h"
#import "LSCircleMainPageViewModel.h"
#import "LSCircleListCollectionCellViewModel.h"
#import "LSNewCircleListViewController.h"
@interface LSCircleListViewController ()
@property (nonatomic, strong) LSCircleListView *mainView;
@property (nonatomic, strong) LSCircleListViewModel *viewModel;
@end
@implementation LSCircleListViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - system
- (void)updateViewConstraints {
WS(weakSelf)
[self.mainView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(weakSelf.view);
}];
[super updateViewConstraints];
}
#pragma mark - private
- (void)yc_addSubviews {
[self.view addSubview:self.mainView];
}
- (void)yc_bindViewModel {
@weakify(self);
[[self.viewModel.cellClickSubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^(LSCircleListCollectionCellViewModel *viewModel) {
@strongify(self);
LSCircleMainPageViewModel *mainViewModel = [[LSCircleMainPageViewModel alloc] init];
mainViewModel.headerViewModel.circleId = viewModel.idStr;
mainViewModel.headerViewModel.headerImageStr = viewModel.headerImageStr;
mainViewModel.headerViewModel.title = viewModel.name;
mainViewModel.headerViewModel.numStr = viewModel.peopleNum;
LSCircleMainPageViewController *circleMainVC = [[LSCircleMainPageViewController alloc] initWithViewModel:mainViewModel];
[self.rdv_tabBarController setTabBarHidden:YES animated:YES];
[self.navigationController pushViewController:circleMainVC animated:YES];
}];
[self.viewModel.listHeaderViewModel.addNewSubject subscribeNext:^(id x) {
@strongify(self);
LSNewCircleListViewController *newCircleListVC = [[LSNewCircleListViewController alloc] init];
[self.rdv_tabBarController setTabBarHidden:YES animated:YES];
[self.navigationController pushViewController:newCircleListVC animated:YES];
}];
}
- (void)yc_layoutNavigation {
self.title = @"圈子列表";
[self.rdv_tabBarController setTabBarHidden:NO animated:YES];
}
#pragma mark - layzLoad
- (LSCircleListView *)mainView {
if (!_mainView) {
_mainView = [[LSCircleListView alloc] initWithViewModel:self.viewModel];
}
return _mainView;
}
- (LSCircleListViewModel *)viewModel {
if (!_viewModel) {
_viewModel = [[LSCircleListViewModel alloc] init];
}
return _viewModel;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
对于VC,分为三个模块,下面分别来说一下:
i 第一个模块:系统函数
此函数是从iOS6.0开始在ViewController中新增一个更新约束布局的方法,这个方法默认的实现是调用对应View的updateConstraints
。ViewController的View在更新视图布局时,会先调用ViewController的updateViewConstraints 方法。我们可以通过重写这个方法去更新当前View的内部布局,而不用再继承这个View去重写-updateConstraints方法。我们在重写这个方法时,务必要调用 super 或者 调用当前View的 -updateConstraints 方法。
ⅱ 第二个模块 : 私有函数
前面基类内也提到了这三个函数的具体作用,即
yd_addSubviews : 添加View到ViewController
yd_bindViewModel : 这里绑定了两个跳转事件。
yd_layoutNavigation : 设置了标题为“圈子列表”、及TabBar不隐藏
ⅲ 第三个模块 : 懒加载
2、View的处理
//
// LSCircleListView.m
// ZhongShui
//
// Created by 王隆帅 on 16/3/10.
// Copyright © 2016年 王隆帅. All rights reserved.
//
#import "LSCircleListView.h"
#import "LSCircleListViewModel.h"
#import "LSCircleListHeaderView.h"
#import "LSCircleListSectionHeaderView.h"
#import "LSCircleListTableCell.h"
@interface LSCircleListView () <UITableViewDataSource, UITableViewDelegate>
@property (strong, nonatomic) LSCircleListViewModel *viewModel;
@property (strong, nonatomic) UITableView *mainTableView;
@property (strong, nonatomic) LSCircleListHeaderView *listHeaderView;
@property (strong, nonatomic) LSCircleListSectionHeaderView *sectionHeaderView;
@end
@implementation LSCircleListView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
#pragma mark - system
- (instancetype)initWithViewModel:(id<YCViewModelProtocol>)viewModel {
self.viewModel = (LSCircleListViewModel *)viewModel;
return [super initWithViewModel:viewModel];
}
- (void)updateConstraints {
WS(weakSelf)
[self.mainTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(weakSelf);
}];
[super updateConstraints];
}
#pragma mark - private
- (void)yc_setupViews {
[self addSubview:self.mainTableView];
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
}
- (void)yc_bindViewModel {
[self.viewModel.refreshDataCommand execute:nil];
@weakify(self);
[self.viewModel.refreshUI subscribeNext:^(id x) {
@strongify(self);
[self.mainTableView reloadData];
}];
[self.viewModel.refreshEndSubject subscribeNext:^(id x) {
@strongify(self);
[self.mainTableView reloadData];
switch ([x integerValue]) {
case LSHeaderRefresh_HasMoreData: {
[self.mainTableView.mj_header endRefreshing];
if (self.mainTableView.mj_footer == nil) {
self.mainTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
@strongify(self);
[self.viewModel.nextPageCommand execute:nil];
}];
}
}
break;
case LSHeaderRefresh_HasNoMoreData: {
[self.mainTableView.mj_header endRefreshing];
self.mainTableView.mj_footer = nil;
}
break;
case LSFooterRefresh_HasMoreData: {
[self.mainTableView.mj_header endRefreshing];
[self.mainTableView.mj_footer resetNoMoreData];
[self.mainTableView.mj_footer endRefreshing];
}
break;
case LSFooterRefresh_HasNoMoreData: {
[self.mainTableView.mj_header endRefreshing];
[self.mainTableView.mj_footer endRefreshingWithNoMoreData];
}
break;
case LSRefreshError: {
[self.mainTableView.mj_footer endRefreshing];
[self.mainTableView.mj_header endRefreshing];
}
break;
default:
break;
}
}];
}
#pragma mark - lazyLoad
- (LSCircleListViewModel *)viewModel {
if (!_viewModel) {
_viewModel = [[LSCircleListViewModel alloc] init];
}
return _viewModel;
}
- (UITableView *)mainTableView {
if (!_mainTableView) {
_mainTableView = [[UITableView alloc] init];
_mainTableView.delegate = self;
_mainTableView.dataSource = self;
_mainTableView.backgroundColor = GX_BGCOLOR;
_mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_mainTableView.tableHeaderView = self.listHeaderView;
[_mainTableView registerClass:[LSCircleListTableCell class] forCellReuseIdentifier:[NSString stringWithUTF8String:object_getClassName([LSCircleListTableCell class])]];
WS(weakSelf)
_mainTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[weakSelf.viewModel.refreshDataCommand execute:nil];
}];
_mainTableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
[weakSelf.viewModel.nextPageCommand execute:nil];
}];
}
return _mainTableView;
}
- (LSCircleListHeaderView *)listHeaderView {
if (!_listHeaderView) {
_listHeaderView = [[LSCircleListHeaderView alloc] initWithViewModel:self.viewModel.listHeaderViewModel];
_listHeaderView.frame = CGRectMake(0, 0, SCREEN_WIDTH, 160);
}
return _listHeaderView;
}
- (LSCircleListSectionHeaderView *)sectionHeaderView {
if (!_sectionHeaderView) {
_sectionHeaderView = [[LSCircleListSectionHeaderView alloc] initWithViewModel:self.viewModel.sectionHeaderViewModel];
}
return _sectionHeaderView;
}
#pragma mark - delegate
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.viewModel.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LSCircleListTableCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithUTF8String:object_getClassName([LSCircleListTableCell class])] forIndexPath:indexPath];
if (self.viewModel.dataArray.count > indexPath.row) {
cell.viewModel = self.viewModel.dataArray[indexPath.row];
}
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.viewModel.dataArray.count > indexPath.row) {
[self.viewModel.cellClickSubject sendNext:self.viewModel.dataArray[indexPath.row]];
}
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return self.sectionHeaderView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 45;
}
@end
3、LSCircleListModel的处理
//
// LSCircleListModel.h
// ZhongShui
//
// Created by 王隆帅 on 16/3/17.
// Copyright © 2016年 王隆帅. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface LSCircleListModel : NSObject
@property (nonatomic, copy) NSString *idStr;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *intro;
@property (nonatomic, copy) NSString *img;
@property (nonatomic, copy) NSString *memberCount;
@property (nonatomic, copy) NSString *topicCount;
@end
#import "LSCircleListModel.h"
@implementation LSCircleListModel
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
return @{
@"idStr":@"id",
@"title":@"title",
@"intro":@"intro",
@"img":@"img",
@"memberCount":@"MemberCount",
@"topicCount":@"TopicCount",
};
}
@end
4、ViewModel的处理
//
// LSCircleListViewModel.h
// ZhongShui
//
// Created by 王隆帅 on 16/3/10.
// Copyright © 2016年 王隆帅. All rights reserved.
//
#import "YCViewModel.h"
#import "LSCircleListHeaderViewModel.h"
#import "LSCircleListSectionHeaderViewModel.h"
@interface LSCircleListViewModel : YCViewModel
@property (nonatomic, strong) RACSubject *refreshEndSubject;
@property (nonatomic, strong) RACSubject *refreshUI;
@property (nonatomic, strong) RACCommand *refreshDataCommand;
@property (nonatomic, strong) RACCommand *nextPageCommand;
@property (nonatomic, strong) LSCircleListHeaderViewModel *listHeaderViewModel;
@property (nonatomic, strong) LSCircleListSectionHeaderViewModel *sectionHeaderViewModel;
@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) RACSubject *cellClickSubject;
@end
//
// LSCircleListViewModel.m
// ZhongShui
//
// Created by 王隆帅 on 16/3/10.
// Copyright © 2016年 王隆帅. All rights reserved.
//
#import "LSCircleListViewModel.h"
#import "LSCircleListCollectionCellViewModel.h"
#import "LSCircleListModel.h"
@interface LSCircleListViewModel ()
@property (nonatomic, assign) NSInteger currentPage;
@end
@implementation LSCircleListViewModel
- (void)yc_initialize {
@weakify(self);
[self.refreshDataCommand.executionSignals.switchToLatest subscribeNext:^(NSDictionary *dict) {
@strongify(self);
if (dict == nil) {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowErrorStatus(@"网络连接失败");
return;
}
if ([dict[@"status"] integerValue] == 0) {
self.listHeaderViewModel.dataArray = [[[([(NSDictionary *)dict[@"res"] arrayForKey:@"JoinCircles"]).rac_sequence map:^id(NSDictionary *dic) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
return viewModel;
}] array] mutableCopy];
self.dataArray = [[[([(NSDictionary *)dict[@"res"] arrayForKey:@"Circles"]).rac_sequence map:^id(NSDictionary *dic) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
return viewModel;
}] array] mutableCopy];
[self ls_setHeaderRefreshWithArray:dict[@"Circles"]];
[self ls_dismiss];
} else {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowMessage(dict[@"mes"]);
}
}];
[[[self.refreshDataCommand.executing skip:1] take:1] subscribeNext:^(id x) {
@strongify(self);
if ([x isEqualToNumber:@(YES)]) {
[self ls_showWithStatus:@"正在加载"];
}
}];
[self.nextPageCommand.executionSignals.switchToLatest subscribeNext:^(NSDictionary *dict) {
@strongify(self);
if (dict == nil) {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowErrorStatus(@"网络连接失败");
return;
}
if ([dict[@"status"] integerValue] == 0) {
NSMutableArray *recommandArray = [[NSMutableArray alloc] initWithArray:self.dataArray];
for (NSDictionary *subDic in [(NSDictionary *)dict[@"res"] arrayForKey:@"Circles"]) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:subDic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
[recommandArray addObject:viewModel];
}
self.dataArray = recommandArray;
[self ls_setFootRefreshWithArray:dict[@"Circles"]];
[self ls_dismiss];
} else {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowMessage(dict[@"mes"]);
}
}];
}
#pragma mark - private
- (NSMutableDictionary *)requestCircleListWithId:(NSString *)idStr currentPage:(NSString *)currentPage {
idStr = IF_NULL_TO_STRING(idStr);
currentPage = IF_NULL_TO_STRING(currentPage);
NSMutableDictionary * dict = [@{@"MemberID": idStr, @"pageSize": LS_REQUEST_LIST_COUNT, @"pageIndex":currentPage} mutableCopy];
return dict;
}
- (void)ls_setFootRefreshWithArray:(NSArray *)array {
if (array.count < LS_REQUEST_LIST_NUM_COUNT) {
[self.refreshEndSubject sendNext:@(LSFooterRefresh_HasNoMoreData)];
} else {
[self.refreshEndSubject sendNext:@(LSFooterRefresh_HasMoreData)];
}
}
- (void)ls_setHeaderRefreshWithArray:(NSArray *)array {
if (array.count < LS_REQUEST_LIST_NUM_COUNT) {
[self.refreshEndSubject sendNext:@(LSHeaderRefresh_HasNoMoreData)];
} else {
[self.refreshEndSubject sendNext:@(LSHeaderRefresh_HasMoreData)];
}
}
#pragma mark - lazyLoad
- (RACSubject *)refreshUI {
if (!_refreshUI) {
_refreshUI = [RACSubject subject];
}
return _refreshUI;
}
- (RACSubject *)refreshEndSubject {
if (!_refreshEndSubject) {
_refreshEndSubject = [RACSubject subject];
}
return _refreshEndSubject;
}
- (RACCommand *)refreshDataCommand {
if (!_refreshDataCommand) {
@weakify(self);
_refreshDataCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.currentPage = 1;
[self.request POST:LS_URL_CIRCLE_MEMBER_LIST parameters:[self requestCircleListWithId:@"1" currentPage:[NSString stringWithFormat:@"%d",self.currentPage]] success:^(CMRequest *request, NSString *responseString) {
NSDictionary *dict = [responseString objectFromJSONString];
[subscriber sendNext:dict];
[subscriber sendCompleted];
} failure:^(CMRequest *request, NSError *error) {
ShowErrorStatus(@"网络连接失败");
[subscriber sendCompleted];
}];
return nil;
}];
}];
}
return _refreshDataCommand;
}
- (RACCommand *)nextPageCommand {
if (!_nextPageCommand) {
@weakify(self);
_nextPageCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.currentPage ++;
[self.request POST:LS_URL_CIRCLE_TOPIC_LIST parameters:nil success:^(CMRequest *request, NSString *responseString) {
NSDictionary *dict = [responseString objectFromJSONString];
[subscriber sendNext:dict];
[subscriber sendCompleted];
} failure:^(CMRequest *request, NSError *error) {
@strongify(self);
self.currentPage --;
ShowErrorStatus(@"网络连接失败");
[subscriber sendCompleted];
}];
return nil;
}];
}];
}
return _nextPageCommand;
}
- (LSCircleListHeaderViewModel *)listHeaderViewModel {
if (!_listHeaderViewModel) {
_listHeaderViewModel = [[LSCircleListHeaderViewModel alloc] init];
_listHeaderViewModel.title = @"已加入的圈子";
_listHeaderViewModel.cellClickSubject = self.cellClickSubject;
}
return _listHeaderViewModel;
}
- (LSCircleListSectionHeaderViewModel *)sectionHeaderViewModel {
if (!_sectionHeaderViewModel) {
_sectionHeaderViewModel = [[LSCircleListSectionHeaderViewModel alloc] init];
_sectionHeaderViewModel.title = @"推荐圈子";
}
return _sectionHeaderViewModel;
}
- (NSArray *)dataArray {
if (!_dataArray) {
_dataArray = [[NSArray alloc] init];
}
return _dataArray;
}
- (RACSubject *)cellClickSubject {
if (!_cellClickSubject) {
_cellClickSubject = [RACSubject subject];
}
return _cellClickSubject;
}
@end
ViewModel也是分为三个模块,由于代码太多摘重要的讲
ⅰ 第一个模块 : 处理数据、逻辑模块
处理数据这块,先用字典转为Model,在用Model配置ViewModel,ViewModel再去与UI及其逻辑对应。
ⅱ 第二个模块 : 私有函数
对于请求参数字典,可以放在VM中,便于模块化移植,也可以放在公共API中便于管理,看个人选择了,没有绝对的好位置,只有更适合个人的位置。
另外两个函数就是处理下拉及上拉时有没有更多数据的私有函数。
ⅲ 第三个模块 : 懒加载
五、后记
写到这里,我这个究级话痨也是累了,没错,这个设计模式就是话唠标准模式外加装逼标准模式,但是,也是你不得不会的。等你掌握后,那开发速度真是蹭蹭的,你的逻辑思维就好像每天在算10以内加减法。。话以至此,不懂得朋友们,欢迎百度和留言,大家多多交流,我是很喜欢交朋友滴,咱们一起好好学习敲代码,为了new出白富美。
六、满满的基情
感激我那迷人的小妖精~帅神的支持,由于本人比较懒,用了帅神的截图和点评,But,我们的目标都是希望大家共同进步,在我们基情的滋润下茁壮成长,咳咳咳...希望大家多多留言,多交流。喜欢的朋友欢迎关注,我会持续更新文章的,教你做一枚多愁善感的程序员,啊哈哈哈~亲们,记得点赞哦